In this exercise, we will pull together many aspects of what we have learned so far to calibrate the HBV model to weighted data from the Fraser Experimental Forest in Colorado using precipitation/runoff and potential evapotranspiration (PET) data derived from the methods explored in the snowmelt and evapotranspiration modules.

HBV is: In the previous labs, we have worked with single-process models. HBV is more complex process-based model using many parameters to describe the physical processes of the hydrologic cycle https://rpubs.com/sdhakal/848236

This schematic was reproduced from: Durga Lal Shrestha & Dimitri P. Solomatine (2008) Data‐driven approaches for estimating uncertainty in rainfall‐runoff modelling, International Journal of River Basin Management, 6:2, 109-122, DOI: 10.1080/15715124.2008.9635341

HBV versions with a GUI exists, but the data manipulation is more convenient in a coding environment, and we want you to be able to see ‘under the hood’. Instead of dealing with the inner workings of the model too much, we will put the focus on importing and organizing the data, getting the model running, manual optimization.

The HBV realization we will be using is contained in the R package TUWmodel. Please search that package in your browser and familiarize yourself with it (that will be the first step). TUW simply means “Technische Universitaet Wien”, or Vienna University of Technology. The model is slightly different from the original HBV but still similar enough to call it HBV. We also need to understand the arguments and parmeters required for the model.

Type ‘?TUWmodel’ in your console. Note that the package comes with an example dataset, and you can run examples either by copying and pasting the data(), TUWmodel() and plots into your console, or you can find and click ‘Run examples’ under the Examples header and view the script and plot outputs in the Help window. The first example shows how the ‘TUWmodel’ can be run given specifications for a long set of parameters (param). You can provide the model with precip, air temperature, area and ET along with the parameter set and it will simulate SWE, snowmelt and discharge. We can obtain measurements for each of these variables, so we will import all of this data and test the fit of the simulated data.

https://cran.r-project.org/web/packages/TUWmodel/TUWmodel.pdf

Import data:

We will generate a conceptual runoff model starting with:
- RainMeltWeighted_mm –> liquid input to the ground, combination of rain and melt
- PWeighted_mm –> Precipitation measured at the SNOTEL sites, both liquid and frozen. This is NOT water that went into the ground at that point. This is, however, the P input time series for the HBV model.
- SweWeighted_mm –> Snow water equivalent at the SNOTEL sites. We can use this to evaluate our snow routine.
- TempWeighted –> Either daily mean, min, or max. We will use the mean. - Discharge_mm –> Q observed at the outlet.
- ET –> Potential Evapotranspiration calculated using relative humidity and temperature.

Labwork (20 pnts)

Our first steps will be to import and format the data. As you might recall from the previous modules, this step can require the most script and ‘work’, but is critical to a valid model output. In this assignment, we have simplified much of the data collection for you, as the steps are ones you have already completed in previous modules. In the .csv imported below, we started by collecting the same SNOTEL data that you used in the snowmelt module, but downloaded for a greater date range (water years 2018-2022) (date, SWE depth in mm, and daily precipitation (mm)) However, you will notice that the column names of the provided table contain ‘weighted_’.

#rm(list = ls()) # this clears out the Environment ("fresh start!")
#indata <- read_csv("FEF_lumped_daily_2020_2023.csv")

indata <- read.csv("HBV_data/weighted_data_fool_HBV.csv")%>%
  mutate(date = ymd(date)) %>%
  rename(Q_m3_s = m3_s, Q_mm_day = mm_day, runoff_input.mm = input.mm )

str(indata)
'data.frame':   1826 obs. of  17 variables:
 $ date              : Date, format: "2017-10-01" ...
 $ wtr_yr            : int  2018 2018 2018 2018 2018 2018 2018 2018 2018 2018 ...
 $ weighted_swe.mm   : num  13.3 46.7 53.4 53.4 51.2 ...
 $ weighted_precip.mm: num  26.78 7.65 1.91 0 0 ...
 $ weighted_pcumul.mm: num  26.8 34.4 36.3 36.3 36.3 ...
 $ Q_m3_s            : num  0.00862 0.00743 0.00715 0.00704 0.00704 ...
 $ Q_mm_day          : num  0.282 0.243 0.234 0.23 0.231 ...
 $ Tmax_c            : num  5.55 1.95 5.25 12.75 12.75 ...
 $ Tmean_c           : num  0.8 -1.6 -0.7 6.2 6.65 0.25 3.2 2.5 -7.8 -1.8 ...
 $ Tmin_c            : num  -3.95 -5.15 -6.65 -0.35 0.55 ...
 $ RHmin             : num  43.1 60.3 41.2 28.9 27.4 ...
 $ RHmax             : num  90.6 91.8 91.2 86 78.6 ...
 $ month             : int  10 10 10 10 10 10 10 10 10 10 ...
 $ day               : int  1 2 3 4 5 6 7 8 9 10 ...
 $ SWEdiff.mm        : num  0 33.38 6.75 0 -2.28 ...
 $ Pdiff.mm          : num  0 7.65 1.91 0 0 ...
 $ runoff_input.mm   : num  0 0 0 0 2.28 ...

As we noted in the Snowmelt module, factors such as elevation and vegetation will affect the of snow-to-runoff rate. Similarly, temperature and precipitation differ with elevation, meaning that SNOTEL data from a single location may not fully represent conditions across the entire watershed.

To account for these variations, the “weighted” values for this exercise have been adjusted using linear scaling relationships that estimate average conditions across the watershed. The adjustments use elevation-precipitation and elevation-temperature relationships derived from inverse distance weighting to better approximate spatially distributed hydrological inputs rather than relying on a single point measurement.

/precip_spatial_interp.png

If you are interested, you can start exploring spatial interpolation with: 1. Thiessen polygons Thiessen polygons 2. Inverse distance weighting 3. Kringing methods to determine if any of these are applicable to your study area.

Other columns in this data include: Q_m3_s and Q_mm_day - Discharge (Q) collected at the Fool Creek outlet by USFS, in units of cubic meters per second and mm per day from April until October. Tmax_c, Tmin_c and Tmean_c - Typically, we could find daily mean, max and minimum temperatures in the SNOTEL datasets, however, this particular station is missing temperature data (due to restrictions on technical access in 2020), so we retrieved temperature data from GridMET through Climate Engine. This highlights the importance of evaluating each variable for completeness. It will save you the headache of running the entire workflow, only to find that model outputs cannot be simulated for the later half of 2020 due to missing input data. RHmin, RHmax - Relative humidity daily min and max, also from GridMET, accessed through Climate Engine SWEdiff.mm, Pdiff.mm and runoff_input.mm - all daily outputs of a temperature based snowmelt model (the same as in the snowmelt module). runoff_input is the estimated daily input to the stream from melted snow and liquid precipitation combined.

Q1 (4 pnts) Generate script (including plots) that can be used to evaluate the structure of the dataset and the duration and consistency of the data. The submitted plot should show at least two time series of ‘indata’ (2 variables on y, date on x), but be sure to check every column for yourself. Do you see any potential issues like missing data or unexpected values? ANSWER:

Keep in mind that you can plot more than two variables in a single plot, but they will be most helpful if you group variables with a similar y-scale. For example, cumulative precipitation or SWE values will have a different range than variables that represent daily measurements like ‘input_mm’. Alternatively, you can add a secondary y-axis to your plot.

BLANK +
  geom_point(aes(x = date, y = runoff_input.mm, color = "runoff_input.mm"), size = 0.5) +  # Assign a label for legend
  geom_point(aes(x = date, y = Q_mm_day, color = "mm/day"), size = 0.5) +  # Assign a different label
  geom_point(aes(x = date, y = weighted_precip.mm, color = "weighted_precip.mm"), size = 0.5) +  # Assign a different label
  BLANK(aes(x = date, y = Pdiff.mm, color = "Pdiff.mm"), size = 0.5) +
  scale_color_manual(values = c("runoff_input.mm" = "blue", "mm/day" = "red", "weighted_precip.mm" = "green", "Pdiff.mm" = 'purple')) +  # Customize colors
  labs(color = "Discharge Units") +  # Legend title
  theme_minimal()
Error: object 'BLANK' not found

2,930m. watershed_area_m2 <- 2640000

Note that while the stream is snow-covered, there are no stage/flow measurements being made at this site. For many of our calculations to work, we will not want NA in our data frames. In this data set, if we view the tabular data, the end discharge reading (Q_mm_day), is very similar to the first in the following calendar year. For this example, we will then fill the NA values with the mean of the final and first readings for each winter (Oct - April) NA string.

Let’s try our quick check again:

Next, we need to add daily potential evapotranspiration (PET) to the dataset. The evapotranspiration module covered some of the numerous pre-built functions available in various R packages designed for ET estimation. For instance, the ‘Evapotranspiration’ package provides ET estimates derived from approximately 20 distinct equations or variations. These functions require precise data formatting to ensure compatibility with the function arguments. To help you structure your function inputs similarly, many packages come with example datasets in their documentation. SPEI is another package that offers ET functions. The github repository for this package has been updated fairly recently, which can be important to verify.

We chose to write our own function here so you could ‘see under the hood’.
Not all packages are equally maintained, as they are often developed by researchers or modelers to improve the repeatably of their work, and contribute to the broader scientific community. Once funding ceases, or if the original developer moves on to new projects, a custom package may no longer receive updates or support. As R, RStudio and other supporting packages are updated, a package may become depreciated. If the package still functions correctly in your current R version and dependencies, there’s no immediate reason to stop using it. However, if you are concerned that future versions of R or dependencies might break the packages you use, you can check the development and maintenance history of custom or specialized packages (or write a package or function for yourself as exemplified below!).

Here is our Hargreaves function, adapted from the ‘Evapotranspiration’ package to minimize re-formatting of our dataframe.

Now we will format the inputs so the data is easily read by the function, and run the function.

Now we’ll add daily ET into our original dataframe:

Q2. (4 pts) Generate a summary dataframe that summarizes columns of ‘indata’ by water year and helps you evaluate the water balance for each year. (I recommend summarizing SWE, precipitation, runoff input, discharge, and PET) Make sure your summary makes sense. For example, some variables are most useful when summarized as a mean, while others are more helpful when viewed as an annual sum, minimum or maximum. If you are unsure of where to start, try using using group_by() and summarize().

Q3 (3 pts) The provided PET data was calculated just using min and max daily air temperature using a Hargreaves approach. View your annual totals for PET and compare it to the amount of precipitation. Are those values reasonable? What can you say about the timing of water demand (PET) and availability (melt and rain)? IMPORTANT: Use runoff_input_mm here and explain why it wouldn’t make sense to use weighted_precip.mm.
ANSWER:

By subtracting discharge and PET from runoff_input, we’re essentially examining the residual water. This could represent the amount of water available to the system after accounting for the demand (PET) and the outflow (discharge).

Q4 (4pnts) Consider the evapotranspiration module and the discussion potential and actual evapotranspiration. Why do you think PET yields estimates of negative residual water in this high elevation, semi-arid system? ANSWER:

Again, you may have to change some of the script below to accommodate your summary dataframe, and not every column will be helpful for a synopsis figure, but the objective in this chunk is to present the column values of your summary dataframe as a bar chart.

Single model run

Look at the parameters to make sure you know what each parameter does. The way the model is set up, is that everything is hidden inside a function. The user (–> you) only formats the input data and passes the parameters to the function. All model output is contained in “modelRun”. If you look at the Environment, you will notice that modelRun is a list. A list is even more flexible in terms of data storage than a dataframe (a dataframe is actually a special type of list…). While lists are super flexible, they can also be more cumbersome to deal with. I included some code that takes the output from the model run and saves all the important bits and pieces in a convenient dataframe called HBVRun. For this part (single model runs), you will really only need the data contained in the HBVRun dataframe.

single model execution

Objective functions

KGE

Before we can start trying to tune our model to look more like the observed discharge record, it would be helpful to have some sort of quantified metric for how well our modeled data fits the measured data.

There are many different ways to do this, but discussion of the pros and cons of those approaches is beyond this quick introduction to modeling.

Here we will demonstrate the Kling-Gupta efficiency both for runoff as well as for swe.

NSE

Nash-Sutcliffe Efficiency (NSE).

Basically, the NSE looks at how much better your model run did that if you had just used the mean discharge for the data record as your “modelled results”. It does this by comparing how far off the observed values where from the mean discharge to how far off the modeled values were from the observed discharge.

Mathematically, NSE is the sum of the squared differences between the modeled and observed discharge divided by the sum of the squared differences between the observed and mean discharge, subtracted by 1.

\[ NSE = 1 - \frac{\sum_{t = 1}^{T}{(Q_m^t - Q_o^t)^2}}{\sum_{t = 1}^{T}{(Q_o^t - \bar{Q_o})^2}} \] Where \(Q_m^t\) is modeled discharge at time t, \(Q_o^t\) is observed discharge at time t, and \(\bar{Q_o}\) is mean observed discharge.

Calibrate HBV manually

Woohoo! We can now run our model and assess how well it is working!

Now, let’s see how well we can get it to work. The code below runs the model, produces a plot, and calculates the NSE based on discharge.


# set up the parameter vector
model_params <- c(
  1.05, # SCF snow correction factor [-] (e.g., 0.9-1.5);
  1.80, # DDF degree day factor [mm/degC/timestep] (e.g., 0.0-5.0 mm/degC/day);
  2, # Tr threshold temperature above which precipitation is rain [degC] (e.g., 1.0-3.0 degC);
  0, # Ts threshold temperature below which precipitation is snow [degC] (e.g., -3.0-1.0 degC);
  -0.336, # Tm threshold temperature above which melt starts [degC] (e.g., -2.0-2.0 degC);
  0.2, # LPrat parameter related to the limit for potential evaporation [-] (e.g., 0.0-1.0);
  121, # FC field capacity, i.e., max soil moisture storage [mm] (e.g., 0-600 mm);
  2.52, # BETA the non linear parameter for runoff production [-] (e.g., 0.0-20.0);
  0.473, # k0 storage coefficient for very fast response [timestep] (e.g., 0.0-2.0 days);
  9.06, # k1 storage coefficient for fast response [timestep] (e.g., 2.0-30.0 days);
  142, # k2 storage coefficient for slow response [timestep] (e.g., 30.0-250.0 days);
  50.1, # lsuz threshold storage state, i.e., the very fast response start if exceeded [mm] (e.g., 1.0-100.0 mm);
  2.38, # cperc constant percolation rate [mm/timestep] (e.g., 0.0-8.0 mm/day);
  10, # bmax maximum base at low flows [timestep] (e.g., 0.0-30.0 days);
  25 # croute free scaling parameter [timestep^2/mm] (e.g., 0.0-50.0 days^2/mm);
)


# set time period
model_in <- indata %>%
  filter(date >= as_date("2017-10-01") & date <= as_date("2022-09-30"))

# set up the model
## THIS IS THE ACTUAL MODEL EXECUTION
modelRun <- TUWmodel(
  prec = model_in$weighted_precip.mm, # precip input
  airt = model_in$Tmean_c, # air temp input
  ep = model_in$PET_mm, # pet input
  area = 1, # one zone for the entire watershed
  param = model_params # input model parameters
)


# get all outputs into a nice df
HBVRun <- tibble(
  Date = model_in$Date, # date
  P_mm = modelRun$prec, # precip
  Tair = modelRun$airt, # air temp
  SWEobs = model_in$weighted_swe.mm, # observed swe
  PET = modelRun$ep,# pet
  Qobs = model_in$Q_mm_day, # observed discharge
  Qsim = modelRun$q[1, ], # simulated discharge
  Qsurf = modelRun$q0[1, ], # surface runoff
  Qsubsurf = modelRun$q1[1, ], # subsurface flow
  Qbase = modelRun$q2[1, ], # groundwater flow
  Rain = modelRun$rain[1, ], # simulated rain
  Snow = modelRun$snow[1, ], # simulated snowfall
  Melt = modelRun$melt[1, ], # simulated melt
  SWEsim = modelRun$swe[1, ], # simulated swe
  Soilmoist = modelRun$moist[1, ], # simulated soil storage
  AET = modelRun$eta[1, ], # simulated evapotranspiration
  StorageUpper = modelRun$suz[1, ], # upper storage value
  StorageLower = modelRun$slz[1, ] # lower storage value
)

#Trim out the warm up period
OutTrim <- HBVRun %>% slice(366:n())

#Calculate NSE
NSE <- 1 - ((sum((OutTrim$Qsim - OutTrim$Qobs) ^ 2)) / 
                 sum((OutTrim$Qobs - mean(OutTrim$Qobs)) ^ 2))

print(NSE)
[1] -0.3496303

Plot observed and model simulations

Generate plots that include and compare the different modeled fluxes from your best NSE. Some of those fluxes can be immediately compared to observed data (e.g., runoff or SWE), while others only exist in simulated form (e.g., storages or outflows of the various runoff components) and need to be assessed with the perceptual model in mind.
Make sure that the axes are properly labeled when you create plots. The script below will get you started.

# Add date to the HBVRun result tibble
HBVRun <- as.data.frame(HBVRun)
HBVRun$Date <- model_in$date
str(HBVRun)
'data.frame':   1826 obs. of  18 variables:
 $ P_mm        : num  26.78 7.65 1.91 0 0 ...
 $ Tair        : num  0.8 -1.6 -0.7 6.2 6.65 0.25 3.2 2.5 -7.8 -1.8 ...
 $ SWEobs      : num  13.3 46.7 53.4 53.4 51.2 ...
 $ PET         : num  1.3 1.15 1.23 1.84 1.76 ...
 $ Qobs        : num  0.282 0.243 0.234 0.23 0.231 ...
 $ Qsim        : num  0.0135 0.0418 0.0661 0.0531 0.0398 ...
 $ Qsurf       : num  0 0 0 0 0 0 0 0 0 0 ...
 $ Qsubsurf    : num  0.1343 0 0 0 0.0421 ...
 $ Qbase       : num  0.0342 0.0434 0.0431 0.0569 0.0732 ...
 $ Rain        : num  10.7 0 0 0 0 ...
 $ Snow        : num  16.07 7.65 1.91 0 0 ...
 $ Melt        : num  2.04 0 0 11.76 12.57 ...
 $ SWEsim      : num  14.825 22.858 24.866 13.101 0.526 ...
 $ Soilmoist   : num  60.1 60.1 60.1 68 75.9 ...
 $ AET         : num  1.3 0 0 1.84 1.76 ...
 $ StorageUpper: num  1.36 0 0 0 0.52 ...
 $ StorageLower: num  4.85 6.16 6.12 8.08 10.39 ...
 $ Date        : Date, format: "2017-10-01" ...
# Round the NSE for display
nse_label <- paste("NSE =", round(NSE, 3))

q_plot <- ggplot(data = HBVRun) +
  geom_line(aes(x = Date, y = Qobs, color = "Qobs")) +  # Qobs
  geom_line(aes(x = Date, y = Qsim, color = "Qsim")) +  # Qsim
  annotate("text",
           x = max(HBVRun$Date) - 100,  # Move label # days from the end
           y = max(HBVRun$Qobs, na.rm = TRUE),
           label = nse_label,
           hjust = 1, vjust = 1.5, size = 5, fontface = "bold") +
  labs(x = NULL, y = "Q (mm/day)", color = NULL, title = "Qobs and Qsim")


# Make it interactive with plotly
ggplotly(q_plot)
NA

Q6. (5 pts) What is the role of the model parameters in representing hydrological processes? What is one effective method to calibrate these parameters without relying on manual adjustment?

Answer:

More exploratory/demonstrative plots

# Swe
swe_plot <- ggplot(data = HBVRun) +
  geom_line(aes(x = Date, y = SWEobs, color = "SWEobs")) + # SWEobs
  geom_line(aes(BLANK)) + # SWEsim
  labs(x = {}, y = "SWE (mm)", color = {})
ggplotly(swe_plot)
Error: object 'BLANK' not found
# Q0, Q1, Q2
q_bucket_plot <- ggplot(data = HBVRun) +
  geom_line(aes(x = Date, y = Qsurf, color = "Qsurf")) + # Qsurf
  geom_line(aes(x = Date, y = Qsubsurf, color = "Qsubsurf")) + #Qsubsurf
  geom_line(aes(x = Date, y = Qbase, color = "Qbase")) + # Qbase
  labs(x = {}, y = "Q (mm)", color = {}, title = "Q0, Q1, and Q2")
ggplotly(q_bucket_plot)
# PET and AET
pet_plot <- ggplot(data = HBVRun) +
  geom_line(aes(x = Date, y = PET, color = "PET")) + # PET
  geom_line(aes(x = Date, y = AET, color = "AET")) + # AET
  labs(x = {}, y = "Flux (mm)", color = {}, title = "PET and AET")
ggplotly(pet_plot)
# Storages
storage_plot <- ggplot(data = HBVRun) +
  geom_line(aes(x = Date, y = Soilmoist, color = "Soil moisture storage")) + # soil moisture storage
  geom_line(aes(x = Date, y = StorageUpper, color = "Upper storage")) + # upper storage
  geom_line(aes(x = Date, y = StorageLower, color = "Lower storage")) + # lower storage
  labs(x = {}, y = "Storage (mm)", color = {}, title = "Storages")
ggplotly(storage_plot)
LS0tCnRpdGxlOiAiSEJWIE1vZGVsIgphdXRob3I6ICJZT1VSIE5BTUUgSEVSRSIKb3V0cHV0OiBodG1sX25vdGVib29rCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKCiNMb2FkIHBhY2thZ2VzCiMgcGtnVGVzdCBpcyBhIGhlbHBlciBmdW5jdGlvbiB0byBsb2FkIHBhY2thZ2VzIGFuZCBpbnN0YWxsIHBhY2thZ2VzIG9ubHkgd2hlbiB0aGV5IGFyZSBub3QgaW5zdGFsbGVkIHlldC4KCnBrZ1Rlc3QgPC0gZnVuY3Rpb24oeCkKewogIGlmICh4ICVpbiUgcm93bmFtZXMoaW5zdGFsbGVkLnBhY2thZ2VzKCkpID09IEZBTFNFKSB7CiAgICBpbnN0YWxsLnBhY2thZ2VzKHgsIGRlcGVuZGVuY2llcz0gVFJVRSkKICB9CiAgbGlicmFyeSh4LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpCn0KbmVlZGVkUGFja2FnZXMgPC0gYygidGlkeXZlcnNlIiwgImx1YnJpZGF0ZSIsICJicm9vbSIsICJUVVdtb2RlbCIsICJwbG90bHkiKSAKCmZvciAocGFja2FnZSBpbiBuZWVkZWRQYWNrYWdlcyl7cGtnVGVzdChwYWNrYWdlKX0KYGBgCgpJbiB0aGlzIGV4ZXJjaXNlLCB3ZSB3aWxsIHB1bGwgdG9nZXRoZXIgbWFueSBhc3BlY3RzIG9mIHdoYXQgd2UgaGF2ZSBsZWFybmVkIHNvIGZhciB0byBjYWxpYnJhdGUgdGhlIEhCViBtb2RlbCB0byB3ZWlnaHRlZCBkYXRhIGZyb20gdGhlIEZyYXNlciBFeHBlcmltZW50YWwgRm9yZXN0IGluIENvbG9yYWRvIHVzaW5nIHByZWNpcGl0YXRpb24vcnVub2ZmIGFuZCBwb3RlbnRpYWwgZXZhcG90cmFuc3BpcmF0aW9uIChQRVQpIGRhdGEgZGVyaXZlZCBmcm9tIHRoZSBtZXRob2RzIGV4cGxvcmVkIGluIHRoZSBzbm93bWVsdCBhbmQgZXZhcG90cmFuc3BpcmF0aW9uIG1vZHVsZXMuIAoKSEJWIGlzOgpJbiB0aGUgcHJldmlvdXMgbGFicywgd2UgaGF2ZSB3b3JrZWQgd2l0aCBzaW5nbGUtcHJvY2VzcyBtb2RlbHMuIEhCViBpcyBtb3JlIGNvbXBsZXggcHJvY2Vzcy1iYXNlZCBtb2RlbCB1c2luZyBtYW55IHBhcmFtZXRlcnMgdG8gZGVzY3JpYmUgdGhlIHBoeXNpY2FsIHByb2Nlc3NlcyBvZiB0aGUgaHlkcm9sb2dpYyBjeWNsZQpodHRwczovL3JwdWJzLmNvbS9zZGhha2FsLzg0ODIzNgoKIVtdKGltYWdlcy9IQlYtc2NoZW0tU2hyZXN0aGEtU29sb21hbnRpbmUtMjAwOC5wbmcgIkhCViBNb2RlbCIpClRoaXMgc2NoZW1hdGljIHdhcyByZXByb2R1Y2VkIGZyb206IER1cmdhIExhbCBTaHJlc3RoYSAmIERpbWl0cmkgUC4gU29sb21hdGluZSAoMjAwOCkgRGF0YeKAkGRyaXZlbiBhcHByb2FjaGVzIGZvciBlc3RpbWF0aW5nIHVuY2VydGFpbnR5IGluIHJhaW5mYWxs4oCQcnVub2ZmIG1vZGVsbGluZywgSW50ZXJuYXRpb25hbCBKb3VybmFsIG9mIFJpdmVyIEJhc2luIE1hbmFnZW1lbnQsIDY6MiwgMTA5LTEyMiwgRE9JOiAxMC4xMDgwLzE1NzE1MTI0LjIwMDguOTYzNTM0MQoKSEJWIHZlcnNpb25zIHdpdGggYSBHVUkgZXhpc3RzLCBidXQgdGhlIGRhdGEgbWFuaXB1bGF0aW9uIGlzIG1vcmUgY29udmVuaWVudCBpbiBhIGNvZGluZyBlbnZpcm9ubWVudCwgYW5kIHdlIHdhbnQgeW91IHRvIGJlIGFibGUgdG8gc2VlICd1bmRlciB0aGUgaG9vZCcuIEluc3RlYWQgb2YgZGVhbGluZyB3aXRoIHRoZSBpbm5lciB3b3JraW5ncyBvZiB0aGUgbW9kZWwgdG9vIG11Y2gsIHdlIHdpbGwgcHV0IHRoZSBmb2N1cyBvbiBpbXBvcnRpbmcgYW5kIG9yZ2FuaXppbmcgdGhlIGRhdGEsIGdldHRpbmcgdGhlIG1vZGVsIHJ1bm5pbmcsIG1hbnVhbCBvcHRpbWl6YXRpb24uICAKClRoZSBIQlYgcmVhbGl6YXRpb24gd2Ugd2lsbCBiZSB1c2luZyBpcyBjb250YWluZWQgaW4gdGhlIFIgcGFja2FnZSBUVVdtb2RlbC4gUGxlYXNlIHNlYXJjaCB0aGF0IHBhY2thZ2UgaW4geW91ciBicm93c2VyIGFuZCBmYW1pbGlhcml6ZSB5b3Vyc2VsZiB3aXRoIGl0ICh0aGF0IHdpbGwgYmUgdGhlIGZpcnN0IHN0ZXApLiBUVVcgc2ltcGx5IG1lYW5zICJUZWNobmlzY2hlIFVuaXZlcnNpdGFldCBXaWVuIiwgb3IgVmllbm5hIFVuaXZlcnNpdHkgb2YgVGVjaG5vbG9neS4gVGhlIG1vZGVsIGlzIHNsaWdodGx5IGRpZmZlcmVudCBmcm9tIHRoZSBvcmlnaW5hbCBIQlYgYnV0IHN0aWxsIHNpbWlsYXIgZW5vdWdoIHRvIGNhbGwgaXQgSEJWLiBXZSBhbHNvIG5lZWQgdG8gdW5kZXJzdGFuZCB0aGUgYXJndW1lbnRzIGFuZCBwYXJtZXRlcnMgcmVxdWlyZWQgZm9yIHRoZSBtb2RlbC4gCgpUeXBlICc/VFVXbW9kZWwnIGluIHlvdXIgY29uc29sZS4gTm90ZSB0aGF0IHRoZSBwYWNrYWdlIGNvbWVzIHdpdGggYW4gZXhhbXBsZSBkYXRhc2V0LCBhbmQgeW91IGNhbiBydW4gZXhhbXBsZXMgZWl0aGVyIGJ5IGNvcHlpbmcgYW5kIHBhc3RpbmcgdGhlIGRhdGEoKSwgVFVXbW9kZWwoKSBhbmQgcGxvdHMgaW50byB5b3VyIGNvbnNvbGUsIG9yIHlvdSBjYW4gZmluZCBhbmQgY2xpY2sgJ1J1biBleGFtcGxlcycgdW5kZXIgdGhlICoqRXhhbXBsZXMqKiBoZWFkZXIgYW5kIHZpZXcgdGhlIHNjcmlwdCBhbmQgcGxvdCBvdXRwdXRzIGluIHRoZSBIZWxwIHdpbmRvdy4gVGhlIGZpcnN0IGV4YW1wbGUgc2hvd3MgaG93IHRoZSAgJ1RVV21vZGVsJyBjYW4gYmUgcnVuIGdpdmVuIHNwZWNpZmljYXRpb25zIGZvciBhIGxvbmcgc2V0IG9mIHBhcmFtZXRlcnMgKHBhcmFtKS4gWW91IGNhbiBwcm92aWRlIHRoZSBtb2RlbCB3aXRoIHByZWNpcCwgYWlyIHRlbXBlcmF0dXJlLCBhcmVhIGFuZCBFVCBhbG9uZyB3aXRoIHRoZSBwYXJhbWV0ZXIgc2V0IGFuZCBpdCB3aWxsIHNpbXVsYXRlIFNXRSwgc25vd21lbHQgYW5kIGRpc2NoYXJnZS4gV2UgY2FuIG9idGFpbiBtZWFzdXJlbWVudHMgZm9yIGVhY2ggb2YgdGhlc2UgdmFyaWFibGVzLCBzbyB3ZSB3aWxsIGltcG9ydCBhbGwgb2YgdGhpcyBkYXRhIGFuZCB0ZXN0IHRoZSBmaXQgb2YgdGhlIHNpbXVsYXRlZCBkYXRhLiAKCmh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9UVVdtb2RlbC9UVVdtb2RlbC5wZGYgIAoKIyMjIEltcG9ydCBkYXRhOiAKV2Ugd2lsbCBnZW5lcmF0ZSBhIGNvbmNlcHR1YWwgcnVub2ZmIG1vZGVsIHN0YXJ0aW5nIHdpdGg6PGJyPgotIFJhaW5NZWx0V2VpZ2h0ZWRfbW0gLS0+IGxpcXVpZCBpbnB1dCB0byB0aGUgZ3JvdW5kLCBjb21iaW5hdGlvbiBvZiByYWluIGFuZCBtZWx0PGJyPgotIFBXZWlnaHRlZF9tbSAtLT4gUHJlY2lwaXRhdGlvbiBtZWFzdXJlZCBhdCB0aGUgU05PVEVMIHNpdGVzLCBib3RoIGxpcXVpZCBhbmQgZnJvemVuLiBUaGlzIGlzIE5PVCB3YXRlciB0aGF0IHdlbnQgaW50byB0aGUgZ3JvdW5kIGF0IHRoYXQgcG9pbnQuIFRoaXMgaXMsIGhvd2V2ZXIsIHRoZSBQIGlucHV0IHRpbWUgc2VyaWVzIGZvciB0aGUgSEJWIG1vZGVsLjxicj4KLSBTd2VXZWlnaHRlZF9tbSAtLT4gU25vdyB3YXRlciBlcXVpdmFsZW50IGF0IHRoZSBTTk9URUwgc2l0ZXMuIFdlIGNhbiB1c2UgdGhpcyB0byBldmFsdWF0ZSBvdXIgc25vdyByb3V0aW5lLjxicj4KLSBUZW1wV2VpZ2h0ZWQgLS0+IEVpdGhlciBkYWlseSBtZWFuLCBtaW4sIG9yIG1heC4gV2Ugd2lsbCB1c2UgdGhlIG1lYW4uCi0gRGlzY2hhcmdlX21tIC0tPiBRIG9ic2VydmVkIGF0IHRoZSBvdXRsZXQuPGJyPgotIEVUIC0tPiBQb3RlbnRpYWwgRXZhcG90cmFuc3BpcmF0aW9uIGNhbGN1bGF0ZWQgdXNpbmcgcmVsYXRpdmUgaHVtaWRpdHkgYW5kIHRlbXBlcmF0dXJlLjxicj4KCiMjIyBSZXBvIGxpbmsKW0Rvd25sb2FkIHRoZSByZXBvIGZvciB0aGlzIGxhYiBIRVJFXShodHRwczovL2dpdGh1Yi5jb20vdHBjb3Zpbm8vMDhfaGJ2X21vZGVscy0uZ2l0KXt0YXJnZXQ9Il9ibGFuayJ9CgojIyMgTGFid29yayAoMjAgcG50cykKCk91ciBmaXJzdCBzdGVwcyB3aWxsIGJlIHRvIGltcG9ydCBhbmQgZm9ybWF0IHRoZSBkYXRhLiBBcyB5b3UgbWlnaHQgcmVjYWxsIGZyb20gdGhlIHByZXZpb3VzIG1vZHVsZXMsIHRoaXMgc3RlcCBjYW4gcmVxdWlyZSB0aGUgbW9zdCBzY3JpcHQgYW5kICd3b3JrJywgYnV0IGlzIGNyaXRpY2FsIHRvIGEgdmFsaWQgbW9kZWwgb3V0cHV0LiBJbiB0aGlzIGFzc2lnbm1lbnQsIHdlIGhhdmUgc2ltcGxpZmllZCBtdWNoIG9mIHRoZSBkYXRhIGNvbGxlY3Rpb24gZm9yIHlvdSwgYXMgdGhlIHN0ZXBzIGFyZSBvbmVzIHlvdSBoYXZlIGFscmVhZHkgY29tcGxldGVkIGluIHByZXZpb3VzIG1vZHVsZXMuIApJbiB0aGUgLmNzdiBpbXBvcnRlZCBiZWxvdywgd2Ugc3RhcnRlZCBieSBjb2xsZWN0aW5nIHRoZSBzYW1lIFNOT1RFTCBkYXRhIHRoYXQgeW91IHVzZWQgaW4gdGhlIHNub3dtZWx0IG1vZHVsZSwgYnV0IGRvd25sb2FkZWQgZm9yIGEgZ3JlYXRlciBkYXRlIHJhbmdlICh3YXRlciB5ZWFycyAyMDE4LTIwMjIpIChkYXRlLCBTV0UgZGVwdGggaW4gbW0sIGFuZCBkYWlseSBwcmVjaXBpdGF0aW9uIChtbSkpIEhvd2V2ZXIsIHlvdSB3aWxsIG5vdGljZSB0aGF0IHRoZSBjb2x1bW4gbmFtZXMgb2YgdGhlIHByb3ZpZGVkIHRhYmxlIGNvbnRhaW4gJ3dlaWdodGVkXycuIAoKYGBge3J9CiNybShsaXN0ID0gbHMoKSkgIyB0aGlzIGNsZWFycyBvdXQgdGhlIEVudmlyb25tZW50ICgiZnJlc2ggc3RhcnQhIikKI2luZGF0YSA8LSByZWFkX2NzdigiRkVGX2x1bXBlZF9kYWlseV8yMDIwXzIwMjMuY3N2IikKCmluZGF0YSA8LSByZWFkLmNzdigiSEJWX2RhdGEvd2VpZ2h0ZWRfZGF0YV9mb29sX0hCVi5jc3YiKSU+JQogIG11dGF0ZShkYXRlID0geW1kKGRhdGUpKSAlPiUKICByZW5hbWUoUV9tM19zID0gbTNfcywgUV9tbV9kYXkgPSBtbV9kYXksIHJ1bm9mZl9pbnB1dC5tbSA9IGlucHV0Lm1tICkKCnN0cihpbmRhdGEpCmBgYAoKQXMgd2Ugbm90ZWQgaW4gdGhlIFNub3dtZWx0IG1vZHVsZSwgZmFjdG9ycyBzdWNoIGFzIGVsZXZhdGlvbiBhbmQgdmVnZXRhdGlvbiB3aWxsIGFmZmVjdCB0aGUgb2Ygc25vdy10by1ydW5vZmYgcmF0ZS4gU2ltaWxhcmx5LCB0ZW1wZXJhdHVyZSBhbmQgcHJlY2lwaXRhdGlvbiBkaWZmZXIgd2l0aCBlbGV2YXRpb24sIG1lYW5pbmcgdGhhdCBTTk9URUwgZGF0YSBmcm9tIGEgc2luZ2xlIGxvY2F0aW9uIG1heSBub3QgZnVsbHkgcmVwcmVzZW50IGNvbmRpdGlvbnMgYWNyb3NzIHRoZSBlbnRpcmUgd2F0ZXJzaGVkLgoKVG8gYWNjb3VudCBmb3IgdGhlc2UgdmFyaWF0aW9ucywgdGhlICJ3ZWlnaHRlZCIgdmFsdWVzIGZvciB0aGlzIGV4ZXJjaXNlIGhhdmUgYmVlbiBhZGp1c3RlZCB1c2luZyBsaW5lYXIgc2NhbGluZyByZWxhdGlvbnNoaXBzIHRoYXQgZXN0aW1hdGUgYXZlcmFnZSBjb25kaXRpb25zIGFjcm9zcyB0aGUgd2F0ZXJzaGVkLiBUaGUgYWRqdXN0bWVudHMgdXNlIGVsZXZhdGlvbi1wcmVjaXBpdGF0aW9uIGFuZCBlbGV2YXRpb24tdGVtcGVyYXR1cmUgcmVsYXRpb25zaGlwcyBkZXJpdmVkIGZyb20gaW52ZXJzZSBkaXN0YW5jZSB3ZWlnaHRpbmcgdG8gYmV0dGVyIGFwcHJveGltYXRlIHNwYXRpYWxseSBkaXN0cmlidXRlZCBoeWRyb2xvZ2ljYWwgaW5wdXRzIHJhdGhlciB0aGFuIHJlbHlpbmcgb24gYSBzaW5nbGUgcG9pbnQgbWVhc3VyZW1lbnQuCgovcHJlY2lwX3NwYXRpYWxfaW50ZXJwLnBuZwoKSWYgeW91IGFyZSBpbnRlcmVzdGVkLCB5b3UgY2FuIHN0YXJ0IGV4cGxvcmluZyBzcGF0aWFsIGludGVycG9sYXRpb24gd2l0aDoKMS4gVGhpZXNzZW4gcG9seWdvbnMKIVtUaGllc3NlbiBwb2x5Z29uc10oaHR0cHM6Ly91cGxvYWQud2lraW1lZGlhLm9yZy93aWtpcGVkaWEvY29tbW9ucy90aHVtYi9kL2Q5L1Zvcm9ub2lfZ3Jvd3RoX2V1Y2xpZGVhbi5naWYvNDQwcHgtVm9yb25vaV9ncm93dGhfZXVjbGlkZWFuLmdpZikKMi4gSW52ZXJzZSBkaXN0YW5jZSB3ZWlnaHRpbmcKMy4gS3JpbmdpbmcgbWV0aG9kcwp0byBkZXRlcm1pbmUgaWYgYW55IG9mIHRoZXNlIGFyZSBhcHBsaWNhYmxlIHRvIHlvdXIgc3R1ZHkgYXJlYS4KCk90aGVyIGNvbHVtbnMgaW4gdGhpcyBkYXRhIGluY2x1ZGU6CioqUV9tM19zIGFuZCBRX21tX2RheSoqIC0gRGlzY2hhcmdlIChRKSBjb2xsZWN0ZWQgYXQgdGhlIEZvb2wgQ3JlZWsgb3V0bGV0IGJ5IFVTRlMsIGluIHVuaXRzIG9mIGN1YmljIG1ldGVycyBwZXIgc2Vjb25kIGFuZCBtbSBwZXIgZGF5IGZyb20gQXByaWwgdW50aWwgT2N0b2Jlci4KKipUbWF4X2MsIFRtaW5fYyBhbmQgVG1lYW5fYyoqIC0gVHlwaWNhbGx5LCB3ZSBjb3VsZCBmaW5kIGRhaWx5IG1lYW4sIG1heCBhbmQgbWluaW11bSB0ZW1wZXJhdHVyZXMgaW4gdGhlIFNOT1RFTCBkYXRhc2V0cywgaG93ZXZlciwgdGhpcyBwYXJ0aWN1bGFyIHN0YXRpb24gaXMgbWlzc2luZyB0ZW1wZXJhdHVyZSBkYXRhIChkdWUgdG8gcmVzdHJpY3Rpb25zIG9uIHRlY2huaWNhbCBhY2Nlc3MgaW4gMjAyMCksIHNvIHdlIHJldHJpZXZlZCB0ZW1wZXJhdHVyZSBkYXRhIGZyb20gR3JpZE1FVCB0aHJvdWdoIFtDbGltYXRlIEVuZ2luZV0oaHR0cHM6Ly9hcHAuY2xpbWF0ZWVuZ2luZS5vcmcvY2xpbWF0ZUVuZ2luZSkuIFRoaXMgaGlnaGxpZ2h0cyB0aGUgaW1wb3J0YW5jZSBvZiBldmFsdWF0aW5nIGVhY2ggdmFyaWFibGUgZm9yIGNvbXBsZXRlbmVzcy4gSXQgd2lsbCBzYXZlIHlvdSB0aGUgaGVhZGFjaGUgb2YgcnVubmluZyB0aGUgZW50aXJlIHdvcmtmbG93LCBvbmx5IHRvIGZpbmQgdGhhdCBtb2RlbCBvdXRwdXRzIGNhbm5vdCBiZSBzaW11bGF0ZWQgZm9yIHRoZSBsYXRlciBoYWxmIG9mIDIwMjAgZHVlIHRvIG1pc3NpbmcgaW5wdXQgZGF0YS4KKipSSG1pbiwgUkhtYXgqKiAtIFJlbGF0aXZlIGh1bWlkaXR5IGRhaWx5IG1pbiBhbmQgbWF4LCBhbHNvIGZyb20gR3JpZE1FVCwgYWNjZXNzZWQgdGhyb3VnaCBDbGltYXRlIEVuZ2luZQoqKlNXRWRpZmYubW0sIFBkaWZmLm1tIGFuZCBydW5vZmZfaW5wdXQubW0qKiAtIGFsbCBkYWlseSBvdXRwdXRzIG9mIGEgdGVtcGVyYXR1cmUgYmFzZWQgc25vd21lbHQgbW9kZWwgKHRoZSBzYW1lIGFzIGluIHRoZSBzbm93bWVsdCBtb2R1bGUpLiBydW5vZmZfaW5wdXQgaXMgdGhlIGVzdGltYXRlZCBkYWlseSBpbnB1dCB0byB0aGUgc3RyZWFtIGZyb20gbWVsdGVkIHNub3cgYW5kIGxpcXVpZCBwcmVjaXBpdGF0aW9uIGNvbWJpbmVkLgoKKipRMSAoNCBwbnRzKSBHZW5lcmF0ZSBzY3JpcHQgKGluY2x1ZGluZyBwbG90cykgdGhhdCBjYW4gYmUgdXNlZCB0byBldmFsdWF0ZSB0aGUgc3RydWN0dXJlIG9mIHRoZSBkYXRhc2V0IGFuZCB0aGUgZHVyYXRpb24gYW5kIGNvbnNpc3RlbmN5IG9mIHRoZSBkYXRhLiBUaGUgc3VibWl0dGVkIHBsb3Qgc2hvdWxkIHNob3cgYXQgbGVhc3QgdHdvIHRpbWUgc2VyaWVzIG9mICdpbmRhdGEnICgyIHZhcmlhYmxlcyBvbiB5LCBkYXRlIG9uIHgpLCBidXQgYmUgc3VyZSB0byBjaGVjayBldmVyeSBjb2x1bW4gZm9yIHlvdXJzZWxmLiBEbyB5b3Ugc2VlIGFueSBwb3RlbnRpYWwgaXNzdWVzIGxpa2UgbWlzc2luZyBkYXRhIG9yIHVuZXhwZWN0ZWQgdmFsdWVzPyoqCkFOU1dFUjoKCgoKCktlZXAgaW4gbWluZCB0aGF0IHlvdSBjYW4gcGxvdCBtb3JlIHRoYW4gdHdvIHZhcmlhYmxlcyBpbiBhIHNpbmdsZSBwbG90LCBidXQgdGhleSB3aWxsIGJlIG1vc3QgaGVscGZ1bCBpZiB5b3UgZ3JvdXAgdmFyaWFibGVzIHdpdGggYSBzaW1pbGFyIHktc2NhbGUuIEZvciBleGFtcGxlLCBjdW11bGF0aXZlIHByZWNpcGl0YXRpb24gb3IgU1dFIHZhbHVlcyB3aWxsIGhhdmUgYSBkaWZmZXJlbnQgcmFuZ2UgdGhhbiB2YXJpYWJsZXMgdGhhdCByZXByZXNlbnQgZGFpbHkgbWVhc3VyZW1lbnRzIGxpa2UgJ2lucHV0X21tJy4gQWx0ZXJuYXRpdmVseSwgeW91IGNhbiBhZGQgYSBzZWNvbmRhcnkgeS1heGlzIHRvIHlvdXIgcGxvdC4KCmBgYHtyfQpCTEFOSyArCiAgZ2VvbV9wb2ludChhZXMoeCA9IGRhdGUsIHkgPSBydW5vZmZfaW5wdXQubW0sIGNvbG9yID0gInJ1bm9mZl9pbnB1dC5tbSIpLCBzaXplID0gMC41KSArICAjIEFzc2lnbiBhIGxhYmVsIGZvciBsZWdlbmQKICBnZW9tX3BvaW50KGFlcyh4ID0gZGF0ZSwgeSA9IFFfbW1fZGF5LCBjb2xvciA9ICJtbS9kYXkiKSwgc2l6ZSA9IDAuNSkgKyAgIyBBc3NpZ24gYSBkaWZmZXJlbnQgbGFiZWwKICBnZW9tX3BvaW50KGFlcyh4ID0gZGF0ZSwgeSA9IHdlaWdodGVkX3ByZWNpcC5tbSwgY29sb3IgPSAid2VpZ2h0ZWRfcHJlY2lwLm1tIiksIHNpemUgPSAwLjUpICsgICMgQXNzaWduIGEgZGlmZmVyZW50IGxhYmVsCiAgQkxBTksoYWVzKHggPSBkYXRlLCB5ID0gUGRpZmYubW0sIGNvbG9yID0gIlBkaWZmLm1tIiksIHNpemUgPSAwLjUpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygicnVub2ZmX2lucHV0Lm1tIiA9ICJibHVlIiwgIm1tL2RheSIgPSAicmVkIiwgIndlaWdodGVkX3ByZWNpcC5tbSIgPSAiZ3JlZW4iLCAiUGRpZmYubW0iID0gJ3B1cnBsZScpKSArICAjIEN1c3RvbWl6ZSBjb2xvcnMKICBsYWJzKGNvbG9yID0gIkRpc2NoYXJnZSBVbml0cyIpICsgICMgTGVnZW5kIHRpdGxlCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKIDIsOTMwbS4Kd2F0ZXJzaGVkX2FyZWFfbTIgPC0gMjY0MDAwMCAKCk5vdGUgdGhhdCB3aGlsZSB0aGUgc3RyZWFtIGlzIHNub3ctY292ZXJlZCwgdGhlcmUgYXJlIG5vIHN0YWdlL2Zsb3cgbWVhc3VyZW1lbnRzIGJlaW5nIG1hZGUgYXQgdGhpcyBzaXRlLiBGb3IgbWFueSBvZiBvdXIgY2FsY3VsYXRpb25zIHRvIHdvcmssIHdlIHdpbGwgbm90IHdhbnQgTkEgaW4gb3VyIGRhdGEgZnJhbWVzLiBJbiB0aGlzIGRhdGEgc2V0LCBpZiB3ZSB2aWV3IHRoZSB0YWJ1bGFyIGRhdGEsIHRoZSBlbmQgZGlzY2hhcmdlIHJlYWRpbmcgKFFfbW1fZGF5KSwgaXMgdmVyeSBzaW1pbGFyIHRvIHRoZSBmaXJzdCBpbiB0aGUgZm9sbG93aW5nIGNhbGVuZGFyIHllYXIuIEZvciB0aGlzIGV4YW1wbGUsIHdlIHdpbGwgdGhlbiBmaWxsIHRoZSBOQSB2YWx1ZXMgd2l0aCB0aGUgbWVhbiBvZiB0aGUgZmluYWwgYW5kIGZpcnN0IHJlYWRpbmdzIGZvciBlYWNoIHdpbnRlciAoT2N0IC0gQXByaWwpIE5BIHN0cmluZy4KCmBgYHtyfQojIFJhdGhlciB0aGFuIHNjcm9sbGluZyB0aHJvdWdoIGEgZGF0YWZyYW1lLCB0aGlzIGdpdmVzIHVzIGEgc3VtIG9mICdOQScgcm9zIGluIHRoaXMgY29sdW1uCnN1bShpcy5uYShpbmRhdGEkUV9tbV9kYXkpKQpgYGAKCmBgYHtyfQojIEZ1bmN0aW9uIHRvIGZpbGwgTkEgdmFsdWVzIGluIFFfbW1fZGF5IGR1cmluZyB3aW50ZXIgKE9jdCAtIEFwcikKZmlsbF9uYV93aW50ZXIgPC0gZnVuY3Rpb24oZGYpIHsKICBkZiA8LSBkZiAlPiUKICAgIGFycmFuZ2UoZGF0ZSkgJT4lICMgRW5zdXJlIGRhdGEgaXMgc29ydGVkCiAgICBtdXRhdGUoCiAgICAgIG1vbnRoID0gbW9udGgoZGF0ZSksCiAgICAgIGlzX3dpbnRlciA9IG1vbnRoICVpbiUgYygxMCwgMTEsIDEyLCAxLCAyLCAzLCA0KSAjIElkZW50aWZ5IHdpbnRlciBtb250aHMKICAgICkKICAKICAjIElkZW50aWZ5IE5BIHN0cmV0Y2hlcyBpbiB3aW50ZXIgbW9udGhzCiAgbmFfaW5kaWNlcyA8LSB3aGljaChpcy5uYShkZiRRX21tX2RheSkgJiBkZiRpc193aW50ZXIpCiAgCiAgZm9yIChpZHggaW4gbmFfaW5kaWNlcykgewogICAgIyBGaW5kIHRoZSBsYXN0IG5vbi1OQSBiZWZvcmUgdGhlIGN1cnJlbnQgTkEKICAgIHByZXZfdmFsdWUgPC0gZGYkUV9tbV9kYXlbbWF4KHdoaWNoKCFpcy5uYShkZiRRX21tX2RheVsxOihpZHggLSAxKV0pKSldCiAgICAKICAgICMgRmluZCB0aGUgbmV4dCBub24tTkEgYWZ0ZXIgdGhlIGN1cnJlbnQgTkEKICAgIG5leHRfdmFsdWUgPC0gZGYkUV9tbV9kYXlbbWluKHdoaWNoKCFpcy5uYShkZiRRX21tX2RheVsoaWR4ICsgMSk6bnJvdyhkZildKSkgKyBpZHgpXQogICAgCiAgICAjIFJlcGxhY2UgdGhlIE5BIHdpdGggdGhlIGF2ZXJhZ2Ugb2YgcHJldl92YWx1ZSBhbmQgbmV4dF92YWx1ZQogICAgaWYgKCFpcy5uYShwcmV2X3ZhbHVlKSAmICFpcy5uYShuZXh0X3ZhbHVlKSkgewogICAgICBkZiRRX21tX2RheVtpZHhdIDwtIChwcmV2X3ZhbHVlICsgbmV4dF92YWx1ZSkgLyAyCiAgICB9CiAgfQogIAogIHJldHVybihkZikKfQoKIyBBcHBseSB0aGUgZnVuY3Rpb24KaW5kYXRhIDwtIEJMQU5LKGluZGF0YSkKYGBgCgpMZXQncyB0cnkgb3VyIHF1aWNrIGNoZWNrIGFnYWluOiAKYGBge3J9CiMgTm93IHdlIHNob3VsZCBzZWUgemVybyBOQSBpbiB0aGlzIGNvbHVtbgpzdW0oaXMubmEoaW5kYXRhJFFfbW1fZGF5KSkKYGBgCgpOZXh0LCB3ZSBuZWVkIHRvIGFkZCBkYWlseSBwb3RlbnRpYWwgZXZhcG90cmFuc3BpcmF0aW9uIChQRVQpIHRvIHRoZSBkYXRhc2V0LiBUaGUgZXZhcG90cmFuc3BpcmF0aW9uIG1vZHVsZSBjb3ZlcmVkIHNvbWUgb2YgdGhlIG51bWVyb3VzIHByZS1idWlsdCBmdW5jdGlvbnMgYXZhaWxhYmxlIGluIHZhcmlvdXMgUiBwYWNrYWdlcyBkZXNpZ25lZCBmb3IgRVQgZXN0aW1hdGlvbi4gRm9yIGluc3RhbmNlLCB0aGUgJ0V2YXBvdHJhbnNwaXJhdGlvbicgcGFja2FnZSBwcm92aWRlcyBFVCBlc3RpbWF0ZXMgZGVyaXZlZCBmcm9tIGFwcHJveGltYXRlbHkgMjAgZGlzdGluY3QgZXF1YXRpb25zIG9yIHZhcmlhdGlvbnMuIFRoZXNlIGZ1bmN0aW9ucyByZXF1aXJlIHByZWNpc2UgZGF0YSBmb3JtYXR0aW5nIHRvIGVuc3VyZSBjb21wYXRpYmlsaXR5IHdpdGggdGhlIGZ1bmN0aW9uIGFyZ3VtZW50cy4gVG8gaGVscCB5b3Ugc3RydWN0dXJlIHlvdXIgZnVuY3Rpb24gaW5wdXRzIHNpbWlsYXJseSwgbWFueSBwYWNrYWdlcyBjb21lIHdpdGggZXhhbXBsZSBkYXRhc2V0cyBpbiB0aGVpciBkb2N1bWVudGF0aW9uLiBTUEVJIGlzIGFub3RoZXIgcGFja2FnZSB0aGF0IG9mZmVycyBFVCBmdW5jdGlvbnMuIFRoZSBnaXRodWIgcmVwb3NpdG9yeSBmb3IgdGhpcyBwYWNrYWdlIGhhcyBiZWVuIHVwZGF0ZWQgZmFpcmx5IHJlY2VudGx5LCB3aGljaCBjYW4gYmUgaW1wb3J0YW50IHRvIHZlcmlmeS4gCgpXZSBjaG9zZSB0byB3cml0ZSBvdXIgb3duIGZ1bmN0aW9uIGhlcmUgc28geW91IGNvdWxkICdzZWUgdW5kZXIgdGhlIGhvb2QnLiAgCk5vdCBhbGwgcGFja2FnZXMgYXJlIGVxdWFsbHkgbWFpbnRhaW5lZCwgYXMgdGhleSBhcmUgb2Z0ZW4gZGV2ZWxvcGVkIGJ5IHJlc2VhcmNoZXJzIG9yIG1vZGVsZXJzIHRvIGltcHJvdmUgdGhlIHJlcGVhdGFibHkgb2YgdGhlaXIgd29yaywgYW5kIGNvbnRyaWJ1dGUgdG8gdGhlIGJyb2FkZXIgc2NpZW50aWZpYyBjb21tdW5pdHkuIE9uY2UgZnVuZGluZyBjZWFzZXMsIG9yIGlmIHRoZSBvcmlnaW5hbCBkZXZlbG9wZXIgbW92ZXMgb24gdG8gbmV3IHByb2plY3RzLCBhIGN1c3RvbSBwYWNrYWdlIG1heSBubyBsb25nZXIgcmVjZWl2ZSB1cGRhdGVzIG9yIHN1cHBvcnQuIEFzIFIsIFJTdHVkaW8gYW5kIG90aGVyIHN1cHBvcnRpbmcgcGFja2FnZXMgYXJlIHVwZGF0ZWQsIGEgcGFja2FnZSBtYXkgYmVjb21lIGRlcHJlY2lhdGVkLiBJZiB0aGUgcGFja2FnZSBzdGlsbCBmdW5jdGlvbnMgY29ycmVjdGx5IGluIHlvdXIgY3VycmVudCBSIHZlcnNpb24gYW5kIGRlcGVuZGVuY2llcywgdGhlcmXigJlzIG5vIGltbWVkaWF0ZSByZWFzb24gdG8gc3RvcCB1c2luZyBpdC4gSG93ZXZlciwgaWYgeW91IGFyZSBjb25jZXJuZWQgdGhhdCBmdXR1cmUgdmVyc2lvbnMgb2YgUiBvciBkZXBlbmRlbmNpZXMgbWlnaHQgYnJlYWsgdGhlIHBhY2thZ2VzIHlvdSB1c2UsIHlvdSBjYW4gY2hlY2sgdGhlIGRldmVsb3BtZW50IGFuZCBtYWludGVuYW5jZSBoaXN0b3J5IG9mIGN1c3RvbSBvciBzcGVjaWFsaXplZCBwYWNrYWdlcyAob3Igd3JpdGUgYSBwYWNrYWdlIG9yIGZ1bmN0aW9uIGZvciB5b3Vyc2VsZiBhcyBleGVtcGxpZmllZCBiZWxvdyEpLgoKSGVyZSBpcyBvdXIgSGFyZ3JlYXZlcyBmdW5jdGlvbiwgYWRhcHRlZCBmcm9tIHRoZSAnRXZhcG90cmFuc3BpcmF0aW9uJyBwYWNrYWdlIHRvIG1pbmltaXplIHJlLWZvcm1hdHRpbmcgb2Ygb3VyIGRhdGFmcmFtZS4gCgpgYGB7cn0KIyBGdW5jdGlvbiB0byBjYWxjdWxhdGUgRVQKY2FsY3VsYXRlX0VUIDwtIGZ1bmN0aW9uKGRhdGEsIGNvbnN0YW50cywgdHMgPSAiZGFpbHkiLCBtZXNzYWdlID0gInllcyIsIHNhdmUuY3N2ID0gIm5vIiwgLi4uKSB7CiAgCiAgIyBDaGVjayBmb3IgcmVxdWlyZWQgZGF0YQogIGlmIChpcy5udWxsKGRhdGEkVG1heCkgfCBpcy5udWxsKGRhdGEkVG1pbikpIHsKICAgIHN0b3AoIlJlcXVpcmVkIGRhdGEgbWlzc2luZyBmb3IgJ1RtYXgnIGFuZCAnVG1pbicsIG9yICdUZW1wJyIpCiAgfQogIAogICMgSGFyZ3JlYXZlcy1TYW1hbmkgRVQgQ2FsY3VsYXRpb24KICBUYSA8LSAoZGF0YSRUbWF4ICsgZGF0YSRUbWluKSAvIDIKICBQIDwtIDEwMS4zICogKCgyOTMgLSAwLjAwNjUgKiBjb25zdGFudHMkRWxldikgLyAyOTMpIF4gNS4yNgogIGRlbHRhIDwtIDQwOTggKiAoMC42MTA4ICogZXhwKCgxNy4yNyAqIFRhKSAvIChUYSArIDIzNy4zKSkpIC8gKChUYSArIDIzNy4zKSBeIDIpCiAgZ2FtbWEgPC0gMC4wMDE2MyAqIFAgLyBjb25zdGFudHMkbGFtYmRhCiAgZF9yMiA8LSAxICsgMC4wMzMgKiBjb3MoMiAqIHBpIC8gMzY1ICogZGF0YSRKKQogIGRlbHRhMiA8LSAwLjQwOSAqIHNpbigyICogcGkgLyAzNjUgKiBkYXRhJEogLSAxLjM5KQogIHdfcyA8LSBhY29zKC10YW4oY29uc3RhbnRzJGxhdF9yYWQpICogdGFuKGRlbHRhMikpCiAgTiA8LSAyNCAvIHBpICogd19zCiAgUl9hIDwtICgxNDQwIC8gcGkpICogZF9yMiAqIGNvbnN0YW50cyRHc2MgKiAod19zICogc2luKGNvbnN0YW50cyRsYXRfcmFkKSAqIHNpbihkZWx0YTIpICsgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29zKGNvbnN0YW50cyRsYXRfcmFkKSAqIGNvcyhkZWx0YTIpICogc2luKHdfcykpCiAgQ19IUyA8LSAwLjAwMTg1ICogKGRhdGEkVG1heCAtIGRhdGEkVG1pbikgXiAyIC0gMC4wNDMzICogKGRhdGEkVG1heCAtIGRhdGEkVG1pbikgKyAwLjQwMjMKICBFVF9IUy5EYWlseSA8LSAwLjAxMzUgKiBDX0hTICogUl9hIC8gY29uc3RhbnRzJGxhbWJkYSAqIChkYXRhJFRtYXggLSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhJFRtaW4pIF4gMC41ICogKFRhICsgMTcuOCkKICBFVC5EYWlseSA8LSBFVF9IUy5EYWlseQogIAogICMgQ3JlYXRlIFllYXJNb250aCBDb2x1bW4KICBkYXRhJFllYXJNb250aCA8LSBhcy5EYXRlKHBhc3RlKHllYXIoZGF0YSREYXRlLmRhaWx5KSwgbW9udGgoZGF0YSREYXRlLmRhaWx5KSwgIjAxIiwgc2VwID0gIi0iKSkKICAKICAjIEFubnVhbCBhbmQgTW9udGhseSBBZ2dyZWdhdGlvbnMKICBFVC5Bbm51YWwgPC0gYWdncmVnYXRlKEVULkRhaWx5IH4geWVhcihZZWFyTW9udGgpLCBkYXRhID0gZGF0YSwgRlVOID0gc3VtKQogIEVULk1vbnRobHkgPC0gYWdncmVnYXRlKEVULkRhaWx5IH4gWWVhck1vbnRoLCBkYXRhID0gZGF0YSwgRlVOID0gc3VtKQogIAogICMgRVQgZm9ybXVsYXRpbmcKICBFVF9mb3JtdWxhdGlvbiA8LSAiSGFyZ3JlYXZlcy1TYW1hbmkiCiAgRVRfdHlwZSA8LSAiUmVmZXJlbmNlIENyb3AgRVQiCiAgcmVzdWx0cyA8LSBsaXN0KEVULkRhaWx5ID0gRVQuRGFpbHksIEVULk1vbnRobHkgPSBFVC5Nb250aGx5LCAKICAgICAgICAgICAgICAgICAgRVQuQW5udWFsID0gRVQuQW5udWFsLCBFVF9mb3JtdWxhdGlvbiA9IEVUX2Zvcm11bGF0aW9uLCAKICAgICAgICAgICAgICAgICAgRVRfdHlwZSA9IEVUX3R5cGUpCiAgCiAgIyBTYXZlIHRvIENTViBpZiByZXF1aXJlZAogIGlmIChzYXZlLmNzdiA9PSAieWVzIikgewogICAgZm9yIChpIGluIDE6bGVuZ3RoKHJlc3VsdHMpKSB7CiAgICAgIG5hbWVyIDwtIG5hbWVzKHJlc3VsdHNbaV0pCiAgICAgIHdyaXRlLnRhYmxlKGFzLmNoYXJhY3RlcihuYW1lciksIGZpbGUgPSAiRVRfSGFyZ3JlYXZlc1NhbWFuaS5jc3YiLCAKICAgICAgICAgICAgICAgICAgZGVjID0gIi4iLCBxdW90ZSA9IEZBTFNFLCBjb2wubmFtZXMgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IEYsIGFwcGVuZCA9IFRSVUUsIHNlcCA9ICIsIikKICAgICAgd3JpdGUudGFibGUoZGF0YS5mcmFtZShnZXQobmFtZXIsIHJlc3VsdHMpKSwgZmlsZSA9ICJFVF9IYXJncmVhdmVzU2FtYW5pLmNzdiIsIAogICAgICAgICAgICAgICAgICBjb2wubmFtZXMgPSBGLCBhcHBlbmQgPSBUUlVFLCBzZXAgPSAiLCIpCiAgICB9CiAgICBpbnZpc2libGUocmVzdWx0cykKICB9IGVsc2UgewogICAgcmV0dXJuKHJlc3VsdHMpCiAgfQp9CmBgYAoKTm93IHdlIHdpbGwgZm9ybWF0IHRoZSBpbnB1dHMgc28gdGhlIGRhdGEgaXMgZWFzaWx5IHJlYWQgYnkgdGhlIGZ1bmN0aW9uLCBhbmQgcnVuIHRoZSBmdW5jdGlvbi4KCmBgYHtyfQojIEZvcm1hdCBvdXIgZGF0YSB0byBmaXQgdGhlIGZ1bmN0aW9uClBFVF9kYXRhIDwtIGxpc3QoCiAgVG1heCA9IGluZGF0YSRUbWF4LAogIFRtaW4gPSBpbmRhdGEkVG1pbiwKICBKID0gYXMubnVtZXJpYyhmb3JtYXQoaW5kYXRhJGRhdGUsICIlaiIpKSwKICBEYXRlLmRhaWx5ID0gaW5kYXRhJGRhdGUKKQoKIyBEZWZpbmUgY29uc3RhbnRzCmNvbnN0YW50cyA8LSBsaXN0KAogIEVsZXYgPSAyOTAwLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEVsZXZhdGlvbiBpbiBtZXRlcnMKICBsYW1iZGEgPSAyLjQ1LCAgICAgICAgICAgICAgICAgICAgICAgICAgIyBMYXRlbnQgaGVhdCBvZiB2YXBvcml6YXRpb24gaW4gTUoua2deLTEKICBsYXRfcmFkID0gMzkuODggKiBwaSAvIDE4MCwgICAgICAgICAgICAgIyBMYXRpdHVkZSBpbiByYWRpYW5zCiAgR3NjID0gMC4wODIwICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgU29sYXIgY29uc3RhbnQgaW4gTUoubV4tMi5taW5eLTEKKQoKUEVUX0hhcmdyZWF2ZXMgPC0gY2FsY3VsYXRlX0VUKAogIGRhdGEgPSBQRVRfZGF0YSwKICBjb25zdGFudHMgPSBjb25zdGFudHMsCiAgdHMgPSAiZGFpbHkiLCAgICAgICAgIyBPcHRpb25hbDsgZGVmYXVsdHMgdG8gImRhaWx5IgogIG1lc3NhZ2UgPSAieWVzIiwgICAgICMgT3B0aW9uYWw7IHByaW50cyBzdW1tYXJ5IAogIHNhdmUuY3N2ID0gIm5vIiAgICAgICMgT3B0aW9uYWw7IGRvIG5vdCBzYXZlIHJlc3VsdHMgdG8gYSBDU1YKKQpgYGAKCk5vdyB3ZSdsbCBhZGQgZGFpbHkgRVQgaW50byBvdXIgb3JpZ2luYWwgZGF0YWZyYW1lOgoKYGBge3J9ClBFVF9tbSA8LSBQRVRfSGFyZ3JlYXZlcyRFVC5EYWlseQojIHB1dCB0aGUgYXBwcm94aW1hdGVkIFBFVF9tbSBpbnRvIHRoZSBsYXJnZXIgaW5kYXRhIGRmCmluZGF0YSA8LSBjYmluZChpbmRhdGEsIFBFVF9tbSkgICN3aXRoIGNiaW5kCgpoZWFkKGluZGF0YSkKYGBgCgoqKlEyLiAoNCBwdHMpIEdlbmVyYXRlIGEgc3VtbWFyeSBkYXRhZnJhbWUgdGhhdCBzdW1tYXJpemVzIGNvbHVtbnMgb2YgJ2luZGF0YScgYnkgd2F0ZXIgeWVhciBhbmQgaGVscHMgeW91IGV2YWx1YXRlIHRoZSB3YXRlciBiYWxhbmNlIGZvciBlYWNoIHllYXIuIChJIHJlY29tbWVuZCBzdW1tYXJpemluZyBTV0UsIHByZWNpcGl0YXRpb24sIHJ1bm9mZiBpbnB1dCwgZGlzY2hhcmdlLCBhbmQgUEVUKSoqICBNYWtlIHN1cmUgeW91ciBzdW1tYXJ5IG1ha2VzIHNlbnNlLiBGb3IgZXhhbXBsZSwgc29tZSB2YXJpYWJsZXMgYXJlIG1vc3QgdXNlZnVsIHdoZW4gc3VtbWFyaXplZCBhcyBhIG1lYW4sIHdoaWxlIG90aGVycyBhcmUgbW9yZSBoZWxwZnVsIHdoZW4gdmlld2VkIGFzIGFuIGFubnVhbCBzdW0sIG1pbmltdW0gb3IgbWF4aW11bS4gSWYgeW91IGFyZSB1bnN1cmUgb2Ygd2hlcmUgdG8gc3RhcnQsIHRyeSB1c2luZyB1c2luZyBncm91cF9ieSgpIGFuZCBzdW1tYXJpemUoKS4KCmBgYHtyfQojIEFubnVhbCBzdW1tYXJ5IHN0YXRzCmluZGF0YV9hbmFseXNpcyA8LSBpbmRhdGEgJT4lCiAgc2VsZWN0KC1kYXRlKSAlPiUKICBCTEFOSyh3dHJfeXIpICU+JQogIEJMQU5LKAogICAgd2VpZ2h0ZWRfcHJlY2lwLm1tID0gc3VtKHdlaWdodGVkX3ByZWNpcC5tbSwgbmEucm0gPSBUUlVFKSwKICAgIFRtZWFuX2MgPSBtZWFuKFRtZWFuX2MsIG5hLnJtID0gVFJVRSksCiAgICBzd2UubW1fbWF4ID0gbWF4KHdlaWdodGVkX3N3ZS5tbSwgbmEucm0gPSBUUlVFKSwKICAgIHBjdW11bC5tbV9tYXggPSBtYXgod2VpZ2h0ZWRfcGN1bXVsLm1tLCBuYS5ybSA9IFRSVUUpLAogICAgVG1heCA9IG1heChUbWF4X2MsIG5hLnJtID0gVFJVRSksCiAgICBUbWluID0gbWluKFRtaW5fYywgbmEucm0gPSBUUlVFKSwKICAgIFFfbW1fc3VtID0gc3VtKFFfbW1fZGF5LCBuYS5ybSA9IFRSVUUpLAogICAgUEVUX21tX3N1bSA9IHN1bShQRVRfbW0sIG5hLnJtID0gVFJVRSksIAogICAgcnVub2ZmX2lucHV0Lm1tID0gc3VtKHJ1bm9mZl9pbnB1dC5tbSwgbmEucm0gPSBUUlVFKSwKICApCgppbmRhdGFfYW5hbHlzaXMKCmBgYAoKKipRMyAoMyBwdHMpIFRoZSBwcm92aWRlZCBQRVQgZGF0YSB3YXMgY2FsY3VsYXRlZCBqdXN0IHVzaW5nIG1pbiBhbmQgbWF4IGRhaWx5IGFpciB0ZW1wZXJhdHVyZSB1c2luZyBhIEhhcmdyZWF2ZXMgYXBwcm9hY2guIFZpZXcgeW91ciBhbm51YWwgdG90YWxzIGZvciBQRVQgYW5kIGNvbXBhcmUgaXQgdG8gdGhlIGFtb3VudCBvZiBwcmVjaXBpdGF0aW9uLiBBcmUgdGhvc2UgdmFsdWVzIHJlYXNvbmFibGU/IFdoYXQgY2FuIHlvdSBzYXkgYWJvdXQgdGhlIHRpbWluZyBvZiB3YXRlciBkZW1hbmQgKFBFVCkgYW5kIGF2YWlsYWJpbGl0eSAobWVsdCBhbmQgcmFpbik/IElNUE9SVEFOVDogVXNlIHJ1bm9mZl9pbnB1dF9tbSBoZXJlIGFuZCBleHBsYWluIHdoeSBpdCB3b3VsZG4ndCBtYWtlIHNlbnNlIHRvIHVzZSB3ZWlnaHRlZF9wcmVjaXAubW0uKiogIApBTlNXRVI6IAoKCgoKYGBge3J9CiMgWW91ciBzY3JpcHQgYmVsb3cgc2hvdWxkIGZpdCB5b3VyIHZhcmlhYmxlcyBmcm9tIHlvdXIgc3VtbWFyeSBkYXRhZnJhbWUgYWJvdmUuIEhlcmUgaXMgYW4gZXhhbXBsZSBvZiB3aGF0IGEgc2ltcGxlIHdhdGVyIGJhbGFuY2UgbWlnaHQgbG9vayBsaWtlIGlmIEkgbmFtZWQgbXkgc3VtbWFyeSBkYXRhZnJhbWUgaW5kYXRhX2FuYWx5c2lzOiAKCiMgQ2FsY3VsYXRlIHRoZSByZXNpZHVhbCB3YXRlciBhZnRlciBhY2NvdW50aW5nIGZvciBQRVQgYW5kIGRpc2NoYXJnZQpyZXNpZHVhbF93YXRlciA8LSBpbmRhdGFfYW5hbHlzaXMkcnVub2ZmX2lucHV0Lm1tIC0gKGluZGF0YV9hbmFseXNpcyRQRVRfbW1fc3VtICsgaW5kYXRhX2FuYWx5c2lzJFFfbW1fc3VtKQoKIyBWaWV3IHRoZSByZXNpZHVhbCBmb3IgZWFjaCB5ZWFyCnByaW50KHJlc2lkdWFsX3dhdGVyKQpgYGAKCkJ5IHN1YnRyYWN0aW5nIGRpc2NoYXJnZSBhbmQgUEVUIGZyb20gcnVub2ZmX2lucHV0LCB3ZeKAmXJlIGVzc2VudGlhbGx5IGV4YW1pbmluZyB0aGUgcmVzaWR1YWwgd2F0ZXIuIFRoaXMgY291bGQgcmVwcmVzZW50IHRoZSBhbW91bnQgb2Ygd2F0ZXIgYXZhaWxhYmxlIHRvIHRoZSBzeXN0ZW0gYWZ0ZXIgYWNjb3VudGluZyBmb3IgdGhlIGRlbWFuZCAoUEVUKSBhbmQgdGhlIG91dGZsb3cgKGRpc2NoYXJnZSkuCgoqKlE0ICg0cG50cykgQ29uc2lkZXIgdGhlIGV2YXBvdHJhbnNwaXJhdGlvbiBtb2R1bGUgYW5kIHRoZSBkaXNjdXNzaW9uIHBvdGVudGlhbCBhbmQgYWN0dWFsIGV2YXBvdHJhbnNwaXJhdGlvbi4gV2h5IGRvIHlvdSB0aGluayBQRVQgeWllbGRzIGVzdGltYXRlcyBvZiBuZWdhdGl2ZSByZXNpZHVhbCB3YXRlciBpbiB0aGlzIGhpZ2ggZWxldmF0aW9uLCBzZW1pLWFyaWQgc3lzdGVtPyoqCkFOU1dFUjoKCgoKCkFnYWluLCB5b3UgbWF5IGhhdmUgdG8gY2hhbmdlIHNvbWUgb2YgdGhlIHNjcmlwdCBiZWxvdyB0byBhY2NvbW1vZGF0ZSB5b3VyIHN1bW1hcnkgZGF0YWZyYW1lLCBhbmQgbm90IGV2ZXJ5IGNvbHVtbiB3aWxsIGJlIGhlbHBmdWwgZm9yIGEgc3lub3BzaXMgZmlndXJlLCBidXQgdGhlIG9iamVjdGl2ZSBpbiB0aGlzIGNodW5rIGlzIHRvIHByZXNlbnQgdGhlIGNvbHVtbiB2YWx1ZXMgb2YgeW91ciBzdW1tYXJ5IGRhdGFmcmFtZSBhcyBhIGJhciBjaGFydC4KCmBgYHtyfQojIG1ha2UgbG9uZyBmb3JtIHdpdGggUEVUc3VtIGFuZCBQc3VtIGFzIHRoZSBrZXktdmFsdWUgcGFpcnMgKGV4Y2x1ZGUgd3RyX3lyIGZyb20gKQpkYXRfc3VtX3Bsb3QgPC0gaW5kYXRhX2FuYWx5c2lzICU+JQogIHBpdm90X2xvbmdlcihuYW1lc190byA9ICAia2V5IiwgdmFsdWVzX3RvID0gICJ2YWx1ZSIsIC13dHJfeXIpCgojIGJhciBwbG90IHBmIFBFVCBhbmQgUCBmb3IgZWFjaCB5ZWFyCmdncGxvdChCTEFOSywgYWVzKHggPSBCTEFOSyB5ID0gdmFsdWUsIGZpbGwgPSBrZXkpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKwogIGxhYnMoeCA9ICJXYXRlciBZZWFyIiwgeSA9ICJtbS95ZWFyIiwgZmlsbCA9IHt9KQpgYGAKCmBgYHtyfQojIyMgVElNSU5HCiMgY3JlYXRlIHdlZWtseSBtZWFucyBvciB0b3RhbHMgZm9yIGJvdGggdGltZSBzZXJpZXMgYW5kIHBsb3QgdGhlbSB0b2dldGhlciB0byBkZXRlcm1pbmUgdGhlIHRpbWluZyBvZiBlYWNoIApkYXRfd2Vla2x5IDwtIGluZGF0YSAlPiUKICBncm91cF9ieShXZWVrID0gd2VlayhkYXRlKSkgJT4lCiAgQkxBTksoCiAgICBQRVQgPSBzdW0oUEVUX21tKSwgCiAgICBQID0gc3VtKHJ1bm9mZl9pbnB1dC5tbSkKICAgICkgJT4lCiAgICBwaXZvdF9sb25nZXIobmFtZXNfdG8gPSAia2V5IiwgdmFsdWVzX3RvID0gInZhbHVlIiwgLVdlZWspCgojIGxpbmUgcGxvdCBvZiBQIGFuZCBQRVQgb24gYSB3ZWVrbHkgYmFzaXMuIFVzZSBkYXRfd2Vla2x5IGFzIHRoZSBkYXRhIHNvdXJjZS4KZ2dwbG90KGRhdF93ZWVrbHksIGFlcyh4ID0gV2VlaywgeSA9IHZhbHVlLCBjb2xvciA9IGtleSkpICsKICBCTEFOSygpICsgIyBjcmVhdGUgbGluZSBwbG90CiAgbGFicyh4ID0gIldlZWsgb2YgWWVhciIsIHkgPSAibW0veWVhciIsIGNvbG9yID0ge30pCmBgYAoKIyBTaW5nbGUgbW9kZWwgcnVuCkxvb2sgYXQgdGhlIHBhcmFtZXRlcnMgdG8gbWFrZSBzdXJlIHlvdSBrbm93IHdoYXQgZWFjaCBwYXJhbWV0ZXIgZG9lcy4gVGhlIHdheSB0aGUgbW9kZWwgaXMgc2V0IHVwLCBpcyB0aGF0IGV2ZXJ5dGhpbmcgaXMgaGlkZGVuIGluc2lkZSBhIGZ1bmN0aW9uLiBUaGUgdXNlciAoLS0+IHlvdSkgb25seSBmb3JtYXRzIHRoZSBpbnB1dCBkYXRhIGFuZCBwYXNzZXMgdGhlIHBhcmFtZXRlcnMgdG8gdGhlIGZ1bmN0aW9uLiBBbGwgbW9kZWwgb3V0cHV0IGlzIGNvbnRhaW5lZCBpbiAibW9kZWxSdW4iLiBJZiB5b3UgbG9vayBhdCB0aGUgRW52aXJvbm1lbnQsIHlvdSB3aWxsIG5vdGljZSB0aGF0IG1vZGVsUnVuIGlzIGEgbGlzdC4gQSBsaXN0IGlzIGV2ZW4gbW9yZSBmbGV4aWJsZSBpbiB0ZXJtcyBvZiBkYXRhIHN0b3JhZ2UgdGhhbiBhIGRhdGFmcmFtZSAoYSBkYXRhZnJhbWUgaXMgYWN0dWFsbHkgYSBzcGVjaWFsIHR5cGUgb2YgbGlzdC4uLikuIFdoaWxlIGxpc3RzIGFyZSBzdXBlciBmbGV4aWJsZSwgdGhleSBjYW4gYWxzbyBiZSBtb3JlIGN1bWJlcnNvbWUgdG8gZGVhbCB3aXRoLiBJIGluY2x1ZGVkIHNvbWUgY29kZSB0aGF0IHRha2VzIHRoZSBvdXRwdXQgZnJvbSB0aGUgbW9kZWwgcnVuIGFuZCBzYXZlcyBhbGwgdGhlIGltcG9ydGFudCBiaXRzIGFuZCBwaWVjZXMgaW4gYSBjb252ZW5pZW50IGRhdGFmcmFtZSBjYWxsZWQgSEJWUnVuLiBGb3IgdGhpcyBwYXJ0IChzaW5nbGUgbW9kZWwgcnVucyksIHlvdSB3aWxsIHJlYWxseSBvbmx5IG5lZWQgdGhlIGRhdGEgY29udGFpbmVkIGluIHRoZSBIQlZSdW4gZGF0YWZyYW1lLgogIAojIHNpbmdsZSBtb2RlbCBleGVjdXRpb24KYGBge3J9CiMgc2V0IHVwIHRoZSBwYXJhbWV0ZXIgdmVjdG9yCm1vZGVsX3BhcmFtcyA8LSBjKAogIDEuMDUsICMgU0NGIHNub3cgY29ycmVjdGlvbiBmYWN0b3IgWy1dIChlLmcuLCAwLjktMS41KTsKICAxLjgwLCAjIERERiBkZWdyZWUgZGF5IGZhY3RvciBbbW0vZGVnQy90aW1lc3RlcF0gKGUuZy4sIDAuMC01LjAgbW0vZGVnQy9kYXkpOwogIDIsICMgVHIgdGhyZXNob2xkIHRlbXBlcmF0dXJlIGFib3ZlIHdoaWNoIHByZWNpcGl0YXRpb24gaXMgcmFpbiBbZGVnQ10gKGUuZy4sIDEuMC0zLjAgZGVnQyk7CiAgMCwgIyBUcyB0aHJlc2hvbGQgdGVtcGVyYXR1cmUgYmVsb3cgd2hpY2ggcHJlY2lwaXRhdGlvbiBpcyBzbm93IFtkZWdDXSAoZS5nLiwgLTMuMC0xLjAgZGVnQyk7CiAgLTAuMzM2LCAjIFRtIHRocmVzaG9sZCB0ZW1wZXJhdHVyZSBhYm92ZSB3aGljaCBtZWx0IHN0YXJ0cyBbZGVnQ10gKGUuZy4sIC0yLjAtMi4wIGRlZ0MpOwogIDAuMiwgIyBMUHJhdCBwYXJhbWV0ZXIgcmVsYXRlZCB0byB0aGUgbGltaXQgZm9yIHBvdGVudGlhbCBldmFwb3JhdGlvbiBbLV0gKGUuZy4sIDAuMC0xLjApOwogIDEyMSwgIyBGQyBmaWVsZCBjYXBhY2l0eSwgaS5lLiwgbWF4IHNvaWwgbW9pc3R1cmUgc3RvcmFnZSBbbW1dIChlLmcuLCAwLTYwMCBtbSk7CiAgMi41MiwgIyBCRVRBIHRoZSBub24gbGluZWFyIHBhcmFtZXRlciBmb3IgcnVub2ZmIHByb2R1Y3Rpb24gWy1dIChlLmcuLCAwLjAtMjAuMCk7CiAgMC40NzMsICMgazAgc3RvcmFnZSBjb2VmZmljaWVudCBmb3IgdmVyeSBmYXN0IHJlc3BvbnNlIFt0aW1lc3RlcF0gKGUuZy4sIDAuMC0yLjAgZGF5cyk7CiAgOS4wNiwgIyBrMSBzdG9yYWdlIGNvZWZmaWNpZW50IGZvciBmYXN0IHJlc3BvbnNlIFt0aW1lc3RlcF0gKGUuZy4sIDIuMC0zMC4wIGRheXMpOwogIDE0MiwgIyBrMiBzdG9yYWdlIGNvZWZmaWNpZW50IGZvciBzbG93IHJlc3BvbnNlIFt0aW1lc3RlcF0gKGUuZy4sIDMwLjAtMjUwLjAgZGF5cyk7CiAgNTAuMSwgIyBsc3V6IHRocmVzaG9sZCBzdG9yYWdlIHN0YXRlLCBpLmUuLCB0aGUgdmVyeSBmYXN0IHJlc3BvbnNlIHN0YXJ0IGlmIGV4Y2VlZGVkIFttbV0gKGUuZy4sIDEuMC0xMDAuMCBtbSk7CiAgMi4zOCwgIyBjcGVyYyBjb25zdGFudCBwZXJjb2xhdGlvbiByYXRlIFttbS90aW1lc3RlcF0gKGUuZy4sIDAuMC04LjAgbW0vZGF5KTsKICAxMCwgIyBibWF4IG1heGltdW0gYmFzZSBhdCBsb3cgZmxvd3MgW3RpbWVzdGVwXSAoZS5nLiwgMC4wLTMwLjAgZGF5cyk7CiAgMjUgIyBjcm91dGUgZnJlZSBzY2FsaW5nIHBhcmFtZXRlciBbdGltZXN0ZXBeMi9tbV0gKGUuZy4sIDAuMC01MC4wIGRheXNeMi9tbSk7CikKCiMgc2V0IHRpbWUgcGVyaW9kCm1vZGVsX2luIDwtIGluZGF0YSAlPiUKICBmaWx0ZXIoZGF0ZSA+PSBhc19kYXRlKCIyMDE3LTEwLTAxIikgJiBkYXRlIDw9IGFzX2RhdGUoIjIwMjItMDktMzAiKSkKCiMgc2V0IHVwIHRoZSBtb2RlbAojIyBUSElTIElTIFRIRSBBQ1RVQUwgTU9ERUwgRVhFQ1VUSU9OCm1vZGVsUnVuIDwtIFRVV21vZGVsKAogIHByZWMgPSBtb2RlbF9pbiR3ZWlnaHRlZF9wcmVjaXAubW0sICMgcHJlY2lwIGlucHV0CiAgYWlydCA9IG1vZGVsX2luJFRtZWFuX2MsICMgYWlyIHRlbXAgaW5wdXQKICBlcCA9IG1vZGVsX2luJFBFVF9tbSwgIyBwZXQgaW5wdXQKICBhcmVhID0gMSwgIyBvbmUgem9uZSBmb3IgdGhlIGVudGlyZSB3YXRlcnNoZWQKICBwYXJhbSA9IG1vZGVsX3BhcmFtcyAjIGlucHV0IG1vZGVsIHBhcmFtZXRlcnMKKQoKCiMgZ2V0IGFsbCBvdXRwdXRzIGludG8gYSBuaWNlIGRmCkhCVlJ1biA8LSB0aWJibGUoCiAgRGF0ZSA9IG1vZGVsX2luJERhdGUsICMgZGF0ZQogIFBfbW0gPSBtb2RlbFJ1biRwcmVjLCAjIHByZWNpcAogIFRhaXIgPSBtb2RlbFJ1biRhaXJ0LCAjIGFpciB0ZW1wCiAgU1dFb2JzID0gbW9kZWxfaW4kd2VpZ2h0ZWRfc3dlLm1tLCAjIG9ic2VydmVkIHN3ZQogIFBFVCA9IG1vZGVsUnVuJGVwLCMgcGV0CiAgUW9icyA9IG1vZGVsX2luJFFfbW1fZGF5LCAjIG9ic2VydmVkIGRpc2NoYXJnZQogIFFzaW0gPSBtb2RlbFJ1biRxWzEsIF0sICMgc2ltdWxhdGVkIGRpc2NoYXJnZQogIFFzdXJmID0gbW9kZWxSdW4kcTBbMSwgXSwgIyBzdXJmYWNlIHJ1bm9mZgogIFFzdWJzdXJmID0gbW9kZWxSdW4kcTFbMSwgXSwgIyBzdWJzdXJmYWNlIGZsb3cKICBRYmFzZSA9IG1vZGVsUnVuJHEyWzEsIF0sICMgZ3JvdW5kd2F0ZXIgZmxvdwogIFJhaW4gPSBtb2RlbFJ1biRyYWluWzEsIF0sICMgc2ltdWxhdGVkIHJhaW4KICBTbm93ID0gbW9kZWxSdW4kc25vd1sxLCBdLCAjIHNpbXVsYXRlZCBzbm93ZmFsbAogIE1lbHQgPSBtb2RlbFJ1biRtZWx0WzEsIF0sICMgc2ltdWxhdGVkIG1lbHQKICBTV0VzaW0gPSBtb2RlbFJ1biRzd2VbMSwgXSwgIyBzaW11bGF0ZWQgc3dlCiAgU29pbG1vaXN0ID0gbW9kZWxSdW4kbW9pc3RbMSwgXSwgIyBzaW11bGF0ZWQgc29pbCBzdG9yYWdlCiAgQUVUID0gbW9kZWxSdW4kZXRhWzEsIF0sICMgc2ltdWxhdGVkIGV2YXBvdHJhbnNwaXJhdGlvbgogIFN0b3JhZ2VVcHBlciA9IG1vZGVsUnVuJHN1elsxLCBdLCAjIHVwcGVyIHN0b3JhZ2UgdmFsdWUKICBTdG9yYWdlTG93ZXIgPSBtb2RlbFJ1biRzbHpbMSwgXSAjIGxvd2VyIHN0b3JhZ2UgdmFsdWUKKQpgYGAKCiMjIyAgT2JqZWN0aXZlIGZ1bmN0aW9ucwojIyMjIEtHRQoKQmVmb3JlIHdlIGNhbiBzdGFydCB0cnlpbmcgdG8gdHVuZSBvdXIgbW9kZWwgdG8gbG9vayBtb3JlIGxpa2UgdGhlCm9ic2VydmVkIGRpc2NoYXJnZSByZWNvcmQsIGl0IHdvdWxkIGJlIGhlbHBmdWwgdG8gaGF2ZSBzb21lIHNvcnQgb2YKcXVhbnRpZmllZCBtZXRyaWMgZm9yIGhvdyB3ZWxsIG91ciBtb2RlbGVkIGRhdGEgZml0cyB0aGUgbWVhc3VyZWQgZGF0YS4KClRoZXJlIGFyZSBtYW55IGRpZmZlcmVudCB3YXlzIHRvIGRvIHRoaXMsIGJ1dCBkaXNjdXNzaW9uIG9mIHRoZSBwcm9zIGFuZCBjb25zIG9mIHRob3NlIGFwcHJvYWNoZXMgaXMgYmV5b25kIHRoaXMgcXVpY2sgaW50cm9kdWN0aW9uIHRvIG1vZGVsaW5nLgoKSGVyZSB3ZSB3aWxsIGRlbW9uc3RyYXRlIHRoZSBLbGluZy1HdXB0YSBlZmZpY2llbmN5IGJvdGggZm9yIHJ1bm9mZiBhcyB3ZWxsIGFzIGZvciBzd2UuCmBgYHtyfQojIHNlbGVjdCB0aGUgUW9icyBhbmQgUXNpbSB0aW1lc2VyaWVzCiMgRE9OJ1QgRk9SR0VUIFRPIEVYQ0xVREUgVEhFIEZJUlNUIFlFQVIKUW9icyA8LSBIQlZSdW4kUW9ic1szNjY6bGVuZ3RoKEhCVlJ1biRRb2JzKV0gIyBvYnNlcnZlZCBydW5vZmYgV0lUSE9VVCBXQVJNLVVQIFBFUklPRApRc2ltIDwtIEhCVlJ1biRRc2ltWzM2NjpsZW5ndGgoSEJWUnVuJFFzaW0pXSAjIHNpbXVsYXRlZCBydW5vZmYgV0lUSE9VVCBXQVJNLVVQIFBFUklPRAoKIyBLR0UKa2dlX3JfcSA8LSBjb3IoUW9icywgUXNpbSkKa2dlX2JldGFfcSA8LSBtZWFuKFFzaW0pIC8gbWVhbihRb2JzKQprZ2VfZ2FtbWFfcSA8LSAoc2QoUXNpbSkgLyBtZWFuKFFzaW0pKSAvIChzZChRb2JzKSAvIG1lYW4oUW9icykpCmtnZV9xIDwtIDEgLSBzcXJ0KChrZ2Vfcl9xIC0gMSleMiArIChrZ2VfYmV0YV9xIC0gMSleMiArIChrZ2VfZ2FtbWFfcSAtIDEpXjIpCmtnZV9xCmBgYAoKCmBgYHtyfQojIyBTbm93IC0gd2F0ZXIgZXF1aXZhbGVudApTV0VvYnMgPC0gSEJWUnVuJFNXRW9ic1szNjY6bGVuZ3RoKEhCVlJ1biRTV0VvYnMpXSAjIG9ic2VydmVkIHN3ZSBXSVRIT1VUIFdBUk0tVVAgUEVSSU9EClNXRXNpbSA8LSBIQlZSdW4kU1dFc2ltWzM2NjpsZW5ndGgoSEJWUnVuJFNXRW9icyldICMgc2ltdWxhdGVkIHN3ZSBXSVRIT1VUIFdBUk0tVVAgUEVSSU9ECgojIEtHRQprZ2Vfcl9zd2UgPC0gY29yKFNXRW9icywgU1dFc2ltKQprZ2VfYmV0YV9zd2UgPC0gbWVhbihTV0VzaW0pIC8gbWVhbihTV0VvYnMpCmtnZV9nYW1tYV9zd2UgPC0gKHNkKFNXRXNpbSkgLyBtZWFuKFNXRXNpbSkpIC8gKHNkKFNXRW9icykgLyBtZWFuKFNXRW9icykpCmtnZV9zd2UgPC0gMSAtIHNxcnQoKGtnZV9yX3N3ZSAtIDEpXjIgKyAoa2dlX2JldGFfc3dlIC0gMSleMiArIChrZ2VfZ2FtbWFfc3dlIC0gMSleMikKa2dlX3N3ZQpgYGAKCiMjIyBOU0UKTmFzaC1TdXRjbGlmZmUgRWZmaWNpZW5jeSAoTlNFKS4KCkJhc2ljYWxseSwgdGhlIE5TRSBsb29rcyBhdCBob3cgbXVjaCBiZXR0ZXIgeW91ciBtb2RlbCBydW4gZGlkIHRoYXQgaWYgeW91IGhhZCBqdXN0IHVzZWQgdGhlIG1lYW4gZGlzY2hhcmdlIGZvciB0aGUgZGF0YSByZWNvcmQgYXMgeW91cgoibW9kZWxsZWQgcmVzdWx0cyIuIEl0IGRvZXMgdGhpcyBieSBjb21wYXJpbmcgaG93IGZhciBvZmYgdGhlIG9ic2VydmVkIHZhbHVlcyB3aGVyZSBmcm9tIHRoZSBtZWFuIGRpc2NoYXJnZSB0byBob3cgZmFyIG9mZiB0aGUgbW9kZWxlZCB2YWx1ZXMgd2VyZSBmcm9tIHRoZSBvYnNlcnZlZCBkaXNjaGFyZ2UuCgpNYXRoZW1hdGljYWxseSwgTlNFIGlzIHRoZSBzdW0gb2YgdGhlIHNxdWFyZWQgZGlmZmVyZW5jZXMgYmV0d2VlbiB0aGUKbW9kZWxlZCBhbmQgb2JzZXJ2ZWQgZGlzY2hhcmdlIGRpdmlkZWQgYnkgdGhlIHN1bSBvZiB0aGUgc3F1YXJlZApkaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZSBvYnNlcnZlZCBhbmQgbWVhbiBkaXNjaGFyZ2UsIHN1YnRyYWN0ZWQgYnkgMS4KCiQkCk5TRSA9IDEgLSBcZnJhY3tcc3VtX3t0ID0gMX1ee1R9eyhRX21edCAtIFFfb150KV4yfX17XHN1bV97dCA9IDF9XntUfXsoUV9vXnQgLSBcYmFye1Ffb30pXjJ9fQokJCBXaGVyZSAkUV9tXnQkIGlzIG1vZGVsZWQgZGlzY2hhcmdlIGF0IHRpbWUgdCwgJFFfb150JCBpcyBvYnNlcnZlZApkaXNjaGFyZ2UgYXQgdGltZSB0LCBhbmQgJFxiYXJ7UV9vfSQgaXMgbWVhbiBvYnNlcnZlZCBkaXNjaGFyZ2UuCgpgYGB7cn0KI0NhbGN1bGF0ZSBOU0UgZm9yIHNub3csIFNXRSBpcyBtb2RlbGVkLCBTVEEyIGlzIG1lYXN1cmVkCk5TRV9RIDwtIDEgLSAoKHN1bSgoSEJWUnVuJFFvYnMgLSBIQlZSdW4kUXNpbSkgXiAyKSkgLyAKICAgICAgICAgICAgICAgICBzdW0oKEhCVlJ1biRRc2ltIC0gbWVhbihIQlZSdW4kUXNpbSkpIF4gMikpCgpOU0VfUQpgYGAKCmBgYHtyfQojQ2FsY3VsYXRlIE5TRSBmb3Igc25vdywgU1dFIGlzIG1vZGVsZWQsIFNUQTIgaXMgbWVhc3VyZWQKTlNFc25vIDwtIDEgLSAoKHN1bSgoSEJWUnVuJFNXRW9icyAtIEhCVlJ1biRTV0VzaW0pIF4gMikpIC8gCiAgICAgICAgICAgICAgICAgc3VtKChIQlZSdW4kU1dFc2ltIC0gbWVhbihIQlZSdW4kU1dFc2ltKSkgXiAyKSkKCk5TRXNubwpgYGAKCiMjIENhbGlicmF0ZSBIQlYgbWFudWFsbHkKCldvb2hvbyEgV2UgY2FuIG5vdyBydW4gb3VyIG1vZGVsIGFuZCBhc3Nlc3MgaG93IHdlbGwgaXQgaXMgd29ya2luZyEKCk5vdywgbGV0J3Mgc2VlIGhvdyB3ZWxsIHdlIGNhbiBnZXQgaXQgdG8gd29yay4gVGhlIGNvZGUgYmVsb3cgcnVucyB0aGUgbW9kZWwsIHByb2R1Y2VzIGEgcGxvdCwgYW5kIGNhbGN1bGF0ZXMgdGhlIE5TRSBiYXNlZCBvbiBkaXNjaGFyZ2UuCgpgYGB7cn0KCiMgc2V0IHVwIHRoZSBwYXJhbWV0ZXIgdmVjdG9yCm1vZGVsX3BhcmFtcyA8LSBjKAogIDEuMDUsICMgU0NGIHNub3cgY29ycmVjdGlvbiBmYWN0b3IgWy1dIChlLmcuLCAwLjktMS41KTsKICAxLjgwLCAjIERERiBkZWdyZWUgZGF5IGZhY3RvciBbbW0vZGVnQy90aW1lc3RlcF0gKGUuZy4sIDAuMC01LjAgbW0vZGVnQy9kYXkpOwogIDIsICMgVHIgdGhyZXNob2xkIHRlbXBlcmF0dXJlIGFib3ZlIHdoaWNoIHByZWNpcGl0YXRpb24gaXMgcmFpbiBbZGVnQ10gKGUuZy4sIDEuMC0zLjAgZGVnQyk7CiAgMCwgIyBUcyB0aHJlc2hvbGQgdGVtcGVyYXR1cmUgYmVsb3cgd2hpY2ggcHJlY2lwaXRhdGlvbiBpcyBzbm93IFtkZWdDXSAoZS5nLiwgLTMuMC0xLjAgZGVnQyk7CiAgLTAuMzM2LCAjIFRtIHRocmVzaG9sZCB0ZW1wZXJhdHVyZSBhYm92ZSB3aGljaCBtZWx0IHN0YXJ0cyBbZGVnQ10gKGUuZy4sIC0yLjAtMi4wIGRlZ0MpOwogIDAuMiwgIyBMUHJhdCBwYXJhbWV0ZXIgcmVsYXRlZCB0byB0aGUgbGltaXQgZm9yIHBvdGVudGlhbCBldmFwb3JhdGlvbiBbLV0gKGUuZy4sIDAuMC0xLjApOwogIDEyMSwgIyBGQyBmaWVsZCBjYXBhY2l0eSwgaS5lLiwgbWF4IHNvaWwgbW9pc3R1cmUgc3RvcmFnZSBbbW1dIChlLmcuLCAwLTYwMCBtbSk7CiAgMi41MiwgIyBCRVRBIHRoZSBub24gbGluZWFyIHBhcmFtZXRlciBmb3IgcnVub2ZmIHByb2R1Y3Rpb24gWy1dIChlLmcuLCAwLjAtMjAuMCk7CiAgMC40NzMsICMgazAgc3RvcmFnZSBjb2VmZmljaWVudCBmb3IgdmVyeSBmYXN0IHJlc3BvbnNlIFt0aW1lc3RlcF0gKGUuZy4sIDAuMC0yLjAgZGF5cyk7CiAgOS4wNiwgIyBrMSBzdG9yYWdlIGNvZWZmaWNpZW50IGZvciBmYXN0IHJlc3BvbnNlIFt0aW1lc3RlcF0gKGUuZy4sIDIuMC0zMC4wIGRheXMpOwogIDE0MiwgIyBrMiBzdG9yYWdlIGNvZWZmaWNpZW50IGZvciBzbG93IHJlc3BvbnNlIFt0aW1lc3RlcF0gKGUuZy4sIDMwLjAtMjUwLjAgZGF5cyk7CiAgNTAuMSwgIyBsc3V6IHRocmVzaG9sZCBzdG9yYWdlIHN0YXRlLCBpLmUuLCB0aGUgdmVyeSBmYXN0IHJlc3BvbnNlIHN0YXJ0IGlmIGV4Y2VlZGVkIFttbV0gKGUuZy4sIDEuMC0xMDAuMCBtbSk7CiAgMi4zOCwgIyBjcGVyYyBjb25zdGFudCBwZXJjb2xhdGlvbiByYXRlIFttbS90aW1lc3RlcF0gKGUuZy4sIDAuMC04LjAgbW0vZGF5KTsKICAxMCwgIyBibWF4IG1heGltdW0gYmFzZSBhdCBsb3cgZmxvd3MgW3RpbWVzdGVwXSAoZS5nLiwgMC4wLTMwLjAgZGF5cyk7CiAgMjUgIyBjcm91dGUgZnJlZSBzY2FsaW5nIHBhcmFtZXRlciBbdGltZXN0ZXBeMi9tbV0gKGUuZy4sIDAuMC01MC4wIGRheXNeMi9tbSk7CikKCgojIHNldCB0aW1lIHBlcmlvZAptb2RlbF9pbiA8LSBpbmRhdGEgJT4lCiAgZmlsdGVyKGRhdGUgPj0gYXNfZGF0ZSgiMjAxNy0xMC0wMSIpICYgZGF0ZSA8PSBhc19kYXRlKCIyMDIyLTA5LTMwIikpCgojIHNldCB1cCB0aGUgbW9kZWwKIyMgVEhJUyBJUyBUSEUgQUNUVUFMIE1PREVMIEVYRUNVVElPTgptb2RlbFJ1biA8LSBUVVdtb2RlbCgKICBwcmVjID0gbW9kZWxfaW4kd2VpZ2h0ZWRfcHJlY2lwLm1tLCAjIHByZWNpcCBpbnB1dAogIGFpcnQgPSBtb2RlbF9pbiRUbWVhbl9jLCAjIGFpciB0ZW1wIGlucHV0CiAgZXAgPSBtb2RlbF9pbiRQRVRfbW0sICMgcGV0IGlucHV0CiAgYXJlYSA9IDEsICMgb25lIHpvbmUgZm9yIHRoZSBlbnRpcmUgd2F0ZXJzaGVkCiAgcGFyYW0gPSBtb2RlbF9wYXJhbXMgIyBpbnB1dCBtb2RlbCBwYXJhbWV0ZXJzCikKCgojIGdldCBhbGwgb3V0cHV0cyBpbnRvIGEgbmljZSBkZgpIQlZSdW4gPC0gdGliYmxlKAogIERhdGUgPSBtb2RlbF9pbiREYXRlLCAjIGRhdGUKICBQX21tID0gbW9kZWxSdW4kcHJlYywgIyBwcmVjaXAKICBUYWlyID0gbW9kZWxSdW4kYWlydCwgIyBhaXIgdGVtcAogIFNXRW9icyA9IG1vZGVsX2luJHdlaWdodGVkX3N3ZS5tbSwgIyBvYnNlcnZlZCBzd2UKICBQRVQgPSBtb2RlbFJ1biRlcCwjIHBldAogIFFvYnMgPSBtb2RlbF9pbiRRX21tX2RheSwgIyBvYnNlcnZlZCBkaXNjaGFyZ2UKICBRc2ltID0gbW9kZWxSdW4kcVsxLCBdLCAjIHNpbXVsYXRlZCBkaXNjaGFyZ2UKICBRc3VyZiA9IG1vZGVsUnVuJHEwWzEsIF0sICMgc3VyZmFjZSBydW5vZmYKICBRc3Vic3VyZiA9IG1vZGVsUnVuJHExWzEsIF0sICMgc3Vic3VyZmFjZSBmbG93CiAgUWJhc2UgPSBtb2RlbFJ1biRxMlsxLCBdLCAjIGdyb3VuZHdhdGVyIGZsb3cKICBSYWluID0gbW9kZWxSdW4kcmFpblsxLCBdLCAjIHNpbXVsYXRlZCByYWluCiAgU25vdyA9IG1vZGVsUnVuJHNub3dbMSwgXSwgIyBzaW11bGF0ZWQgc25vd2ZhbGwKICBNZWx0ID0gbW9kZWxSdW4kbWVsdFsxLCBdLCAjIHNpbXVsYXRlZCBtZWx0CiAgU1dFc2ltID0gbW9kZWxSdW4kc3dlWzEsIF0sICMgc2ltdWxhdGVkIHN3ZQogIFNvaWxtb2lzdCA9IG1vZGVsUnVuJG1vaXN0WzEsIF0sICMgc2ltdWxhdGVkIHNvaWwgc3RvcmFnZQogIEFFVCA9IG1vZGVsUnVuJGV0YVsxLCBdLCAjIHNpbXVsYXRlZCBldmFwb3RyYW5zcGlyYXRpb24KICBTdG9yYWdlVXBwZXIgPSBtb2RlbFJ1biRzdXpbMSwgXSwgIyB1cHBlciBzdG9yYWdlIHZhbHVlCiAgU3RvcmFnZUxvd2VyID0gbW9kZWxSdW4kc2x6WzEsIF0gIyBsb3dlciBzdG9yYWdlIHZhbHVlCikKCiNUcmltIG91dCB0aGUgd2FybSB1cCBwZXJpb2QKT3V0VHJpbSA8LSBIQlZSdW4gJT4lIHNsaWNlKDM2NjpuKCkpCgojQ2FsY3VsYXRlIE5TRQpOU0UgPC0gMSAtICgoc3VtKChPdXRUcmltJFFzaW0gLSBPdXRUcmltJFFvYnMpIF4gMikpIC8gCiAgICAgICAgICAgICAgICAgc3VtKChPdXRUcmltJFFvYnMgLSBtZWFuKE91dFRyaW0kUW9icykpIF4gMikpCgpwcmludChOU0UpCmBgYAoKIyMgUGxvdCBvYnNlcnZlZCBhbmQgbW9kZWwgc2ltdWxhdGlvbnMKCkdlbmVyYXRlIHBsb3RzIHRoYXQgaW5jbHVkZSBhbmQgY29tcGFyZSB0aGUgZGlmZmVyZW50IG1vZGVsZWQgZmx1eGVzIGZyb20geW91ciBiZXN0IE5TRS4gU29tZSBvZiB0aG9zZSBmbHV4ZXMgY2FuIGJlIGltbWVkaWF0ZWx5IGNvbXBhcmVkIHRvIG9ic2VydmVkIGRhdGEgKGUuZy4sIHJ1bm9mZiBvciBTV0UpLCB3aGlsZSBvdGhlcnMgb25seSBleGlzdCBpbiBzaW11bGF0ZWQgZm9ybSAoZS5nLiwgc3RvcmFnZXMgb3Igb3V0Zmxvd3Mgb2YgdGhlIHZhcmlvdXMgcnVub2ZmIGNvbXBvbmVudHMpIGFuZCBuZWVkIHRvIGJlIGFzc2Vzc2VkIHdpdGggdGhlIHBlcmNlcHR1YWwgbW9kZWwgaW4gbWluZC4gIApNYWtlIHN1cmUgdGhhdCB0aGUgYXhlcyBhcmUgcHJvcGVybHkgbGFiZWxlZCB3aGVuIHlvdSBjcmVhdGUgcGxvdHMuIFRoZSBzY3JpcHQgYmVsb3cgd2lsbCBnZXQgeW91IHN0YXJ0ZWQuIAoKYGBge3J9CiMgQWRkIGRhdGUgdG8gdGhlIEhCVlJ1biByZXN1bHQgdGliYmxlCkhCVlJ1biA8LSBhcy5kYXRhLmZyYW1lKEhCVlJ1bikKSEJWUnVuJERhdGUgPC0gbW9kZWxfaW4kZGF0ZQpzdHIoSEJWUnVuKQoKIyBSb3VuZCB0aGUgTlNFIGZvciBkaXNwbGF5Cm5zZV9sYWJlbCA8LSBwYXN0ZSgiTlNFID0iLCByb3VuZChOU0UsIDMpKQoKcV9wbG90IDwtIGdncGxvdChkYXRhID0gSEJWUnVuKSArCiAgZ2VvbV9saW5lKGFlcyh4ID0gRGF0ZSwgeSA9IFFvYnMsIGNvbG9yID0gIlFvYnMiKSkgKyAgIyBRb2JzCiAgZ2VvbV9saW5lKGFlcyh4ID0gRGF0ZSwgeSA9IFFzaW0sIGNvbG9yID0gIlFzaW0iKSkgKyAgIyBRc2ltCiAgYW5ub3RhdGUoInRleHQiLAogICAgICAgICAgIHggPSBtYXgoSEJWUnVuJERhdGUpIC0gMTAwLCAgIyBNb3ZlIGxhYmVsICMgZGF5cyBmcm9tIHRoZSBlbmQKICAgICAgICAgICB5ID0gbWF4KEhCVlJ1biRRb2JzLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgIGxhYmVsID0gbnNlX2xhYmVsLAogICAgICAgICAgIGhqdXN0ID0gMSwgdmp1c3QgPSAxLjUsIHNpemUgPSA1LCBmb250ZmFjZSA9ICJib2xkIikgKwogIGxhYnMoeCA9IE5VTEwsIHkgPSAiUSAobW0vZGF5KSIsIGNvbG9yID0gTlVMTCwgdGl0bGUgPSAiUW9icyBhbmQgUXNpbSIpCgoKIyBNYWtlIGl0IGludGVyYWN0aXZlIHdpdGggcGxvdGx5CmdncGxvdGx5KHFfcGxvdCkKCmBgYAoKKipRNi4gKDUgcHRzKSBXaGF0IGlzIHRoZSByb2xlIG9mIHRoZSBtb2RlbCBwYXJhbWV0ZXJzIGluIHJlcHJlc2VudGluZyBoeWRyb2xvZ2ljYWwgcHJvY2Vzc2VzPyBXaGF0IGlzIG9uZSBlZmZlY3RpdmUgbWV0aG9kIHRvIGNhbGlicmF0ZSB0aGVzZSBwYXJhbWV0ZXJzIHdpdGhvdXQgcmVseWluZyBvbiBtYW51YWwgYWRqdXN0bWVudD8qKgoKQW5zd2VyOiAgCgojIyMgTW9yZSBleHBsb3JhdG9yeS9kZW1vbnN0cmF0aXZlIHBsb3RzCgpgYGB7cn0KCiMjIERPTidUIGZvcmdldCB0byByZW1vdmUgdGhlIHdhcm0gdXAgcGVyaW9kIGlmIHlvdSB3YW50IHRvIGNhbGN1bGF0ZSBOU0Ugb3IgS0dFCiMgU3dlCnN3ZV9wbG90IDwtIGdncGxvdChkYXRhID0gSEJWUnVuKSArCiAgZ2VvbV9saW5lKGFlcyh4ID0gRGF0ZSwgeSA9IFNXRW9icywgY29sb3IgPSAiU1dFb2JzIikpICsgIyBTV0VvYnMKICBnZW9tX2xpbmUoYWVzKEJMQU5LKSkgKyAjIFNXRXNpbQogIGxhYnMoeCA9IHt9LCB5ID0gIlNXRSAobW0pIiwgY29sb3IgPSB7fSkKZ2dwbG90bHkoc3dlX3Bsb3QpCmBgYAoKCmBgYHtyfQojIFEwLCBRMSwgUTIKcV9idWNrZXRfcGxvdCA8LSBnZ3Bsb3QoZGF0YSA9IEhCVlJ1bikgKwogIGdlb21fbGluZShhZXMoeCA9IERhdGUsIHkgPSBRc3VyZiwgY29sb3IgPSAiUXN1cmYiKSkgKyAjIFFzdXJmCiAgZ2VvbV9saW5lKGFlcyh4ID0gRGF0ZSwgeSA9IFFzdWJzdXJmLCBjb2xvciA9ICJRc3Vic3VyZiIpKSArICNRc3Vic3VyZgogIGdlb21fbGluZShhZXMoeCA9IERhdGUsIHkgPSBRYmFzZSwgY29sb3IgPSAiUWJhc2UiKSkgKyAjIFFiYXNlCiAgbGFicyh4ID0ge30sIHkgPSAiUSAobW0pIiwgY29sb3IgPSB7fSwgdGl0bGUgPSAiUTAsIFExLCBhbmQgUTIiKQpnZ3Bsb3RseShxX2J1Y2tldF9wbG90KQpgYGAKCmBgYHtyfQojIFBFVCBhbmQgQUVUCnBldF9wbG90IDwtIGdncGxvdChkYXRhID0gSEJWUnVuKSArCiAgZ2VvbV9saW5lKGFlcyh4ID0gRGF0ZSwgeSA9IFBFVCwgY29sb3IgPSAiUEVUIikpICsgIyBQRVQKICBnZW9tX2xpbmUoYWVzKHggPSBEYXRlLCB5ID0gQUVULCBjb2xvciA9ICJBRVQiKSkgKyAjIEFFVAogIGxhYnMoeCA9IHt9LCB5ID0gIkZsdXggKG1tKSIsIGNvbG9yID0ge30sIHRpdGxlID0gIlBFVCBhbmQgQUVUIikKZ2dwbG90bHkocGV0X3Bsb3QpCmBgYAoKYGBge3J9CiMgU3RvcmFnZXMKc3RvcmFnZV9wbG90IDwtIGdncGxvdChkYXRhID0gSEJWUnVuKSArCiAgZ2VvbV9saW5lKGFlcyh4ID0gRGF0ZSwgeSA9IFNvaWxtb2lzdCwgY29sb3IgPSAiU29pbCBtb2lzdHVyZSBzdG9yYWdlIikpICsgIyBzb2lsIG1vaXN0dXJlIHN0b3JhZ2UKICBnZW9tX2xpbmUoYWVzKHggPSBEYXRlLCB5ID0gU3RvcmFnZVVwcGVyLCBjb2xvciA9ICJVcHBlciBzdG9yYWdlIikpICsgIyB1cHBlciBzdG9yYWdlCiAgZ2VvbV9saW5lKGFlcyh4ID0gRGF0ZSwgeSA9IFN0b3JhZ2VMb3dlciwgY29sb3IgPSAiTG93ZXIgc3RvcmFnZSIpKSArICMgbG93ZXIgc3RvcmFnZQogIGxhYnMoeCA9IHt9LCB5ID0gIlN0b3JhZ2UgKG1tKSIsIGNvbG9yID0ge30sIHRpdGxlID0gIlN0b3JhZ2VzIikKZ2dwbG90bHkoc3RvcmFnZV9wbG90KQpgYGAKCgo=